zoukankan      html  css  js  c++  java
  • ConcurrentHashMap1.7源码分析

    参考:https://www.cnblogs.com/liuyun1995/p/8631264.html

    HashMap不是线程安全的,其所有的方法都未同步,虽然可以使用Collections的synchronizedMap方法使其线程安全,但是针对的只是当前的map对象。对此,

    JDK提供了线程安全的Hashtable,其所有的方法都是同步的,即使用Synchronized关键字进行加锁,但是也导致了同一时刻只能有一个线程操作哈希表,影响

    性能,所以又提供了效率较高的线程安全的哈希表ConcurrentHashMap,其内部使用分段锁将锁进行细粒度化,从而使多个线程能够同时操作哈希表,大大提

    高了性能。下面是其内部结构示意图:

    Like {@link Hashtable} but unlike {@link HashMap}, this class
     does <em>not</em> allow {@code null} to be used as a key or value

    和Hashtable一样,不允许null键和null值

    Concurrent1.7和1.8的区别:

    1.7中采用Segment+HashEntry的方式进行实现,并发控制使用ReentrantLock来实现。

      每一个Segment元素存储的是HashEntry数组+链表,和HashMap的数据结构相同

     1.8中放弃了Segment臃肿的设计,取而代之的是采用Node数组+链表+红黑树的数据结构来实现,当链表的长度大于8时会转化为红黑树。

      并发控制使用 Synchronized+CAS来保证

      JDK1.8版本的ConcurrentHashMap的数据结构已经接近HashMap,相对而言,ConcurrentHashMap只是增加了同步的操作来控制并发,从JDK1.7版本的ReentrantLock+Segment+HashEntry,到JDK1.8版本中synchronized+CAS+HashEntry+红黑树,相对而言,总结如下思考

    JDK1.8的实现降低锁的粒度,JDK1.7版本锁的粒度是基于Segment的,包含多个HashEntry,而JDK1.8锁的粒度就是HashEntry(首节点)
    JDK1.8版本的数据结构变得更加简单,使得操作也更加清晰流畅,因为已经使用synchronized来进行同步,所以不需要分段锁的概念,也就不需要Segment这种数据结构了,由于粒度的降低,实现的复杂度也增加了
    JDK1.8使用红黑树来优化链表,基于长度很长的链表的遍历是一个很漫长的过程,而红黑树的遍历效率是很快的(logn),代替一定阈值的链表,这样形成一个最佳拍档


    JDK1.8为什么使用内置锁synchronized来代替重入锁ReentrantLock:
      1、因为粒度降低了,在相对而言的低粒度加锁方式,synchronized并不比ReentrantLock差,在粗粒度加锁中ReentrantLock可能通过Condition来控制各个低粒度的边界,

        更加的灵活,而在低粒度中,Condition的优势就没有了
      2、JVM的开发团队从来都没有放弃synchronized,而且基于JVM的synchronized优化空间更大,使用内嵌的关键字比使用API更加自然
      3、在大量的数据操作下,对于JVM的内存压力,基于API的ReentrantLock会开销更多的内存,虽然不是瓶颈,但是也是一个选择依据

      原文:https://blog.csdn.net/qq296398300/article/details/79074239

    以下源码基于Java 1.7版本

    public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
            implements ConcurrentMap<K, V>, Serializable {
        private static final long serialVersionUID = 7249069246763182397L;

    其成员变量:

      /**
         * The maximum number of segments to allow; used to bound
         * constructor arguments. Must be power of two less than 1 << 24.
       * 分段锁的最大数量
    */ static final int MAX_SEGMENTS = 1 << 16; /** * Number of unsynchronized retries in size and containsValue * methods before resorting to locking. This is used to avoid * unbounded retries if tables undergo continuous modification * which would make it impossible to obtain an accurate result.
       * 分段锁的最小数量
    */ static final int RETRIES_BEFORE_LOCK = 2; /** * Mask value for indexing into segments. The upper bits of a * key's hash code are used to choose the segment.
    * 分段锁的掩码值
    */ final int segmentMask; /** * Shift value for indexing within segments.
    * 分段锁的移位值
    */ final int segmentShift; /** * The segments, each of which is a specialized hash table.
    * 分段锁数组
    */ final Segment<K,V>[] segments;

    Segment是ConcurrentHashpMap的一个静态内部类:

     static final class Segment<K,V> extends ReentrantLock implements Serializable {
     static final int MAX_SCAN_RETRIES =
                Runtime.getRuntime().availableProcessors() > 1 ? 64 : 1;
     transient volatile HashEntry<K,V>[] table;
     transient int count;
     transient int modCount;
    
     transient int threshold;
     final float loadFactor;

    Segment继承了ReentrantLock ,所以其本质上是一个锁。

     分段锁即是由Segment来实现的,它继承于ReentrantLock,用来管理它辖区内的各个HashEntry。ConcurrentHashMap被Segment分成了很多小区,Segment就相当于小区保安,

    HashEntry列表相当于小区业主,小区保安通过加锁的方式,保证每个Segment内都不发生冲突。

    put和remove方法,对于Segment内部元素和计数器的更新,全部处于锁的保护下,如Segment.put()方法的第一行:

    HashEntry<K,V> node = tryLock() ? null : scanAndLockForPut(key, hash, value);
    
    
  • 相关阅读:
    人生转折点:弃文从理
    人生第一站:大三暑假实习僧
    监听器启动顺序和java常见注解
    java常识和好玩的注释
    182. Duplicate Emails (Easy)
    181. Employees Earning More Than Their Managers (Easy)
    180. Consecutive Numbers (Medium)
    178. Rank Scores (Medium)
    177. Nth Highest Salary (Medium)
    176. Second Highest Salary(Easy)
  • 原文地址:https://www.cnblogs.com/yangyongjie/p/10978502.html
Copyright © 2011-2022 走看看