根据 key 计算出对应的 hash 值

根据 key 计算出对应的 hash 值

代码语言:javascript
复制
public V put(K key, V value) { 
       if (value == null)          //ConcurrentHashMap 中不允许用 null 作为映射值
           throw new NullPointerException(); 
       int hash = hash(key.hashCode());        // 计算键对应的散列码
       // 根据散列码找到对应的 Segment 
       return segmentFor(hash).put(key, hash, value, false); 
}

  然后,根据 hash 值找到对应的Segment 对象:

代码语言:javascript
复制
/** 
    * 使用 key 的散列码来得到 segments 数组中对应的 Segment 
    */ 
final Segment<K,V> segmentFor(int hash) { 
   // 将散列值右移 segmentShift 个位,并在高位填充 0 ,然后把得到的值与 segmentMask 相“与”,从而得到 hash 值对应的 segments 数组的下标值,最后根据下标值返回散列码对应的 Segment 对象
       return segments[(hash >>> segmentShift) & segmentMask]; 
}

  最后,在这个 Segment 中执行具体的 put 操作:

代码语言:javascript
复制
    V put(K key, int hash, V value, boolean onlyIfAbsent) { 
           lock();  // 加锁,这里是锁定某个 Segment 对象而非整个 ConcurrentHashMap 
           try { 
               int c = count;
           if (c++ &gt; threshold)     // 如果超过再散列的阈值
               rehash();              // 执行再散列,table 数组的长度将扩充一倍

           HashEntry&lt;K,V&gt;[] tab = table; 
           int index = hash &amp; (tab.length - 1); 
           // 找到散列值对应的具体的那个桶
           HashEntry&lt;K,V&gt; first = tab[index]; 

           HashEntry&lt;K,V&gt; e = first; 
           while (e != null &amp;&amp; (e.hash != hash || !key.equals(e.key))) 
               e = e.next; 

           V oldValue; 
           if (e != null) {            // 如果键值对已经存在
               oldValue = e.value; 
               if (!onlyIfAbsent) 
                   e.value = value;    // 替换 value 值
           } 
           else {                        // 键值对不存在 
               oldValue = null; 
               ++modCount;         // 要添加新节点到链表中,所以 modCont 要加 1  
               // 创建新节点,并添加到链表的头部 
               tab[index] = new HashEntry&lt;K,V&gt;(key, hash, first, value); 
               count = c;               // 写 count 变量
           } 
           return oldValue; 
       } finally { 
           unlock();                     // 解锁
       } 
   }</code></pre></div></div><p>  注意:这里的加锁操作是针对某个具体的 Segment,锁定的是该 Segment 而不是整个  ConcurrentHashMap。因为插入键 / 值对操作只是在这个 Segment  包含的某个桶中完成,不需要锁定整个ConcurrentHashMap。此时,其他写线程对另外 15 个Segment  的加锁并不会因为当前线程对这个 Segment 的加锁而阻塞。同时,所有读线程几乎不会因本线程的加锁而阻塞(除非读线程刚好读到这个  Segment 中某个 HashEntry 的 value 域的值为 null,此时需要加锁后重新读取该值)。

  相比较于 HashTable 和由同步包装器包装的 HashMap每次只能有一个线程执行读或写操作,ConcurrentHashMap 在并发访问性能上有了质的提高。在理想状态下,ConcurrentHashMap 可以支持 16 个线程执行并发写操作(如果并发级别设置为 16),及任意数量线程的读操作。