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);
    
    
  • 相关阅读:
    多线程之同步代码块与同步函数
    图片上传客户端与服务端
    tcp上传学习二--文本文件上传
    javaScript编写9*9口诀
    tcp聊天
    udp聊天室--简易
    往sencha.cmd中恢复设计项时,不论是系统的还是应用的,恢复进去之后都应该一键发布到前端
    一个设计项上的按钮调另一个设计项的列表界面,同时加筛选条件
    点击【****】设计项上的某个按钮,直接调出另一个设计项的【编辑界面】
    前端向后端发送请求,后端返回的一个值的请求的ajax.get();方法
  • 原文地址:https://www.cnblogs.com/yangyongjie/p/10978502.html
Copyright © 2011-2022 走看看