ConcurrentHashMap

简介: ConcurrentHashMap

ConcurrentHashMap(JDK1.8)

  • 流程图:
  • ConcurrentHashMap属性列表

sizeCtl:表初始化和调整大小控制字段.如果为负数,则表正在初始化或调整大小,此时写操作将自旋

Node的Hash节点数据节点类型:MOVED=-1,列表的格式正在转换;TREEBIN=-2,树节点的Hash;RESERVED=-3,临时Hash;HASH_BITS=0x7fffffff,正常节点Hash

  • ConcurrentHashMap的putVal源码
final V putVal(K key, V value, boolean onlyIfAbsent) {
        if (key == null || value == null) throw new NullPointerException();
        //将key转换为hash码,尽可能的保证hash的均匀分布,且hash值必须大于0,因为小于0的是特殊hash
        int hash = spread(key.hashCode());
        int binCount = 0;
        for (Node<K,V>[] tab = table;;) {
            Node<K,V> f; int n, i, fh;
            //如果tab为空
            if (tab == null || (n = tab.length) == 0)
            //则创建table
                tab = initTable();
            //查询tab中第1个元素,如果为空,则将第一个元素直接插入
            else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
                if (casTabAt(tab, i, null,
                             new Node<K,V>(hash, key, value, null)))
                    break;                   // no lock when adding to empty bin
            }
            //hash获取,在JDK1.8中,ConcurrentHashMap中所有列表的第一个node作为锁
            //内置了不同的hash码,来标记不同的状态,
            //将节点的元素数据移植到新表上面
            else if ((fh = f.hash) == MOVED)
                tab = helpTransfer(tab, f);
            else {
                V oldVal = null;
                //对节点f加锁
                synchronized (f) {
                //二次验证节点f
                    if (tabAt(tab, i) == f) {
                        //如果f节点的Hash值正常,也就是大于0
                        if (fh >= 0) {
                            binCount = 1;
                            for (Node<K,V> e = f;; ++binCount) {
                                K ek;
                                //如果存在hash一致的
                                if (e.hash == hash &&
                                    ((ek = e.key) == key ||
                                     (ek != null && key.equals(ek)))) {
                                    oldVal = e.val;
                                    //且允许覆盖,则覆盖
                                    if (!onlyIfAbsent)
                                        e.val = value;
                                    break;
                                }
                                Node<K,V> pred = e;
                                //否则链表尾插入
                                if ((e = e.next) == null) {
                                    pred.next = new Node<K,V>(hash, key,
                                                              value, null);
                                    break;
                                }
                            }
                        }
                        //如果节点f为树类型节点
                        else if (f instanceof TreeBin) {
                            Node<K,V> p;
                            binCount = 2;
                            if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
                                                           value)) != null) {
                                oldVal = p.val;
                                if (!onlyIfAbsent)
                                    p.val = value;
                            }
                        }
                    }
                }
                //如果链表节点个数大于8且table数据个数大于默认上限64,则转化为红黑树
                //如果小于64,则只是尝试扩容
                if (binCount != 0) {
                    if (binCount >= TREEIFY_THRESHOLD)
                        treeifyBin(tab, i);
                    if (oldVal != null)
                        return oldVal;
                    break;
                }
            }
        }
        addCount(1L, binCount);
        return null;
    }
• ConcurrentHashMap#putVal
• initTable()源码
    private final Node<K,V>[] initTable() {
        Node<K,V>[] tab; int sc;
        while ((tab = table) == null || tab.length == 0) {
            //获取系统的sizeCtl赋值给sc,如果sizeCtl小于0,则认为此时的数据已经被其余线程修改,自身进入等待......
            if ((sc = sizeCtl) < 0)
                Thread.yield(); // 当前线程自旋
                //CAS操作
                //获取当前对象的SIZECTL与sc比较,如果系统的SIZECTL等于sc,则将SIZECTL修改为-1
            else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
                try {
                    if ((tab = table) == null || tab.length == 0) {
                        //为Table设置元素存取上限
                        int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
                        @SuppressWarnings("unchecked")
                        Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
                        table = tab = nt;
                        sc = n - (n >>> 2);
                    }
                } finally {
                    sizeCtl = sc;
                }
                break;
            }
        }
        return tab;
    }
  • Unsafe.compareAndSwapInt(Object var1, long var2, int var4, int var5);
  • 乐观锁CAS(比较交换):如果对象var1中的属性var2的值与期望值var4一致,则认为数据在内存没有被修改,此时把var5赋值给var2
  • CAS流程图:
  • 无锁CAS与锁的区别
  • 在性能方面:无锁CAS不需要线程之间切换的开销;但是无锁CAS可能会导致问题一致处于无解的状态,进而一直自旋,导致CPU资源一直占用
  • ConcurrentHashMap、 HashMap、Hashtable对比
ConcurrentHashMap HashMap Hashtable
数据安全? 安全 不安全 安全
性能?
  • HashTable通过synchronized关键字实现了线程安全,但是HashTable在方法级别上使用了synchronized,会使线程独占一张HashTable,性能较低
  • ConcurrentHashMap通过synchronized以及CAS来实现线程安全,但是ConcurrentHashMap是通过对数据节点使用了synchronized,其相对于HashTable来讲,封锁的粒度更低,可以保证线程不是独占HashTable,而是独占了某一部分数据,对于其余的数据依然可以访问,因此其性能相较于HashTable来说,性能更好
目录
相关文章
|
5月前
|
安全 Java 开发者
ConcurrentHashMap详解
ConcurrentHashMap详解
|
安全
HashMap 是线程安全的吗?
HashMap 是线程安全的吗?
72 0
|
12月前
|
安全 算法 Java
ConcurrentHashMap
ConcurrentHashMap
|
存储 安全 算法
HashMap和ConcurrentHashmap
大家好,我是苍何。最近思考了一个问题,为什么会出现公司面试造火箭,工作扭螺丝的现象,包括各种八股文的连环大绝杀问到你不会为主,其实这是考察你的知识面以及掌握的深度,而为什么需要这样呢?归其原因,无非是通过筛选找到那些会思考的人,他们需要的并不是CRUD的工具人,而是会思考能创新的工程师。
45 0
|
安全
Hashtable与ConcurrentHashMap的区别
Hashtable与ConcurrentHashMap的区别
|
存储 安全 Java
HashMap和Hashtable以及ConcurrentHashMap的区别
HashMap是在JDK1.2中引入的Map的实现类。
333 0
|
SQL 安全 网络协议
HashTable,ConcurrentHashMap这些你知道吗
又到了整理技术点的时间了,今天讲述的是ConcurrentHashMap,大家对这个我相信也是很熟悉的,不知是否知道以下面试常问的一些技术点呢?
HashTable,ConcurrentHashMap这些你知道吗
|
存储 安全 JavaScript
HashMap1.8与ConcurrentHashMap1.8线程安全比较
HashMap1.8与ConcurrentHashMap1.8线程安全比较
178 0
|
缓存 安全 Java
ConcurrentHashMap线程安全吗
ConcurrentHashMap 是 Java 并发包中提供的一个线程安全且高效的 HashMap 实现,以弥补 HashMap 不适合在并发环境中操作使用的不足。