ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

广州私立试管婴儿机构做试管选性别包成功最低需要多少钱?

2019-06-07 16:01:13  阅读:250  来源: 互联网

标签:hash 16 试管 私立 试管婴儿 数组 segmentMask segment ssize


广州薇電█ 188★2335★0811 ████试管选性别███供卵试管婴儿代孕███选性别生男孩 ████试管包出生████代孕男孩████代孕包出生███代孕选性别██试管婴儿███代孕生男孩█████ ​助孕人群,不孕不育,发育不良,同性恋,石女,大龄女,子宫肌瘤,子宫内膜炎,子宫内膜移位,少精弱精,等等人群......

看源码前我们必须先知道一下ConcurrentHashMap的基本结构。ConcurrentHashMap是采用分段锁来进行并发控制的。

其中有一个内部类为Segment类用来表示锁。而Segment类里又有一个HashEntry<K,V>[]数组,这个数组才是真正用

来存放我们的key-value的。

大概为如下图结构。一个Segment数组,而Segment数组每个元素为一个HashEntry数组

 

看源码前我们还必须了解的几个默认的常量值:

  • DEFAULT_INITIAL_CAPACITY = 16   容器默认容量为16
  • DEFAULT_LOAD_FACTOR = 0.75f     默认扩容因子是0.75
  • DEFAULT_CONCURRENCY_LEVEL = 16  默认并发度是16
  • MAXIMUM_CAPACITY = 1 << 30      容器最大容量为1073741824
  • MIN_SEGMENT_TABLE_CAPACITY = 2  段的最小大小
  • MAX_SEGMENTS = 1 << 16          段的最大大小
  • RETRIES_BEFORE_LOCK = 2         通过不获取锁的方式尝试获取size的次数

 

以上以及默认值是ConcurrentHashMap中定义好的,下面我们很多地方会用到他们。

先从初始化开始说起

通过我们使用ConcurrentHashMap都是通过 ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();的方式

我们点进去跟踪下源码

复制代码
/**
     * Creates a new, empty map with a default initial capacity (16),
     * load factor (0.75) and concurrencyLevel (16).
     */
    public ConcurrentHashMap() {
        this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
    }
复制代码

可以看到,默认无参构造函数内调用了另一个带参构造函数,而这个构造函数也就是不管你初始化时传进来什么参数,最终都会跳到那个带参构造函数。

点进去看看这个带参构造函数实现了什么功能

复制代码
public ConcurrentHashMap(int initialCapacity,
                             float loadFactor, int concurrencyLevel) {
        if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0)
            throw new IllegalArgumentException();
        if (concurrencyLevel > MAX_SEGMENTS)
            concurrencyLevel = MAX_SEGMENTS;
        // Find power-of-two sizes best matching arguments
        int sshift = 0;
        int ssize = 1;
        while (ssize < concurrencyLevel) {
            ++sshift;
            ssize <<= 1;
        }
        this.segmentShift = 32 - sshift;
        this.segmentMask = ssize - 1;
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        int c = initialCapacity / ssize;
        if (c * ssize < initialCapacity)
            ++c;
        int cap = MIN_SEGMENT_TABLE_CAPACITY;
        while (cap < c)
            cap <<= 1;
        // create segments and segments[0]
        Segment<K,V> s0 =
            new Segment<K,V>(loadFactor, (int)(cap * loadFactor),
                             (HashEntry<K,V>[])new HashEntry[cap]);
        Segment<K,V>[] ss = (Segment<K,V>[])new Segment[ssize];
        UNSAFE.putOrderedObject(ss, SBASE, s0); // ordered write of segments[0]
        this.segments = ss;
    }
复制代码

我们看到该构造函数一共有三个参数,分别是容器的初始化大小、负载因子、并发度,这三个参数如果我们new 一个ConcurrentHashMap时没有指定,

那么将会采用默认的参数,也就是我们本文开始说的那几个常量值。

在这里我对这三个参数做下解释。容器初始化大小是整个map的容量。负载因子是用来计算每个segment里的HashEntry数组扩容时的阈值的。并发度是

用来设置segment数组的长度的。

开头这两个if没什么好说的。就是用来判断我们传进来的参数的正确性。当负载因子,初始容量和并发度不按照规范来时会抛出算术异常。第二个if时当传进来的

并发度大于最大段大小的时候,就将其设置为最大段大小。

这段就比较有意思了。由于segment数组要求长度必须为2的n次方,当我们传进来的并发度不是2的n次方时会计算出一个最接近它的2的n次方值

比如如何我们传进来的并发度为14 15那么通过计算segment数组长度就是16。在上图中我们可以看到两个局部变量ssize和sshift,在循环中如果ssize小于

并发度就将其二进制左移一位,即乘2。因此ssize就是用来保存我们计算出来的最接近并发度的2的n次方值。而ssfhit是用来计算偏移量的。在这里我们又

要说两个很重要的全局常量。segmentMask和segmentShift。其中segmentMask为ssize - 1,由于ssize为2的倍数。那么segmentMask就是奇数。化为

二进制就是全1,而segmentShift为32 - sshift大小。32是key值经过再hash求出来的值的二进制位。segmentMask和segmentShift是用来定位当前元素

在segment数组那个位置,和在HashEntry数组的哪个位置,后面我们会详细说说怎么算的。

这一段代码就是用来确定每个segment里面的hashentry的一些参数和初始化segment数组了。第一个if是防止我们设置的初始化

容量大于最大容量。而c是用来计算每个hashentry数组的容量。由于每个hashentry数组容量也需要为2的n次方,因此这里也需要

一个cap和循环来计算一个2的n次方值,方法和上面一样。这里计算出来的cap值就是最终hashentry数组实际的大小了。

初始化就做了这些工作了。

那么我们在说说最简单的get方法。

get方法就需要用到定位我们的元素了。而定位元素就需要我们上面初始化时设置好的两个值:segmentMask和segmentShift

 上面说了,并发度默认值为16,那么ssize也为16,因此segmentMask为15.由于ssize二进制往左移了4位,那么sshift就是4,

segmentShift就是32-4=28.下面我们就用segmentMask=15,segmentShift为28来说说怎么确定元素位置的。

在这里我们要说下hash值,这里的hash值不是key的hashcode值,而是经过再hash确定下来的一个hash值,目的是为了减少hash冲突。

hash值二进制为32位。

上图两个红框就是分别确定segment数组中的位置和hashentry数组中的位置。

我们可以看到确定segment数组是采用 (h >>> segmentShift) & segmentMask,其中h为再hash过的hash值。将32为的hash值往右移segmentShift位。这里我们假设移了28位。

而segmentMask为15,就是4位都为一的二进制。将高4位与segmentMask相与会等到一个小于16的值,就是当前元素再的segment位置。

确定了所属的segment后。就要确认在的hashentry位置了。通过第二个红框处,我们可以看到确定hashentry的位置没有使用上面两个值了。而是直接使用当前hashentry数组的长度减一

和hash值想与。通过两种不同的算法分别定位segment和hashenrty可以保证元素在segment数组和hashentry数组里面都散列开了。

Put方法

复制代码
public V put(K key, V value) {
        Segment<K,V> s;
        if (value == null)
            throw new NullPointerException();
        int hash = hash(key);
        int j = (hash >>> segmentShift) & segmentMask;
        if ((s = (Segment<K,V>)UNSAFE.getObject          // nonvolatile; recheck
             (segments, (j << SSHIFT) + SBASE)) == null) //  in ensureSegment
            s = ensureSegment(j);
        return s.put(key, hash, value, false);
    }

标签:hash,16,试管,私立,试管婴儿,数组,segmentMask,segment,ssize
来源: https://www.cnblogs.com/rewq/p/10988354.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有