ConcurrentHashMap实现原理
2022-12-12 15:18 杭伟 阅读(26) 评论(0) 编辑 收藏 举报jdk1.7分段锁:
Segment类(小型HashMap),继承ReentrantLock
put简要流程:
先根据key,算出Segment数组的下标index
加锁
生成Entry对象,并添加到Segment的数组或链表上(和HashMap相同)
segments[index].put(key,value);
释放锁
ConcurrentHashMap源码
构造函数:
构造函数中主要生成segement数组,并将Segment对象放入segment数组的第一个位置。
segment数组大小为什么没有直接从参数获取而是取了一个>=传参的2^n的数?
因为计算数组下标时,使用的是hashcode和&table.length-1(与运算) ,在之前HashMap章节有说明这个问题,这里不再详述。
jdk1.8 synchronized + CAS
1.8ConcurrentHashMap采用和HashMap一样的结构:
1.7使用HashEntry,1.8改为Node
1.8put简要流程:
1、根据 key 计算出 hashcode,然后开始遍历 table;
2、判断是否需要初始化;
3、f 即为当前 key 定位出的 Node,如果为空表示当前位置可以写入数据,利用 CAS 尝试写入,失败则自旋保证成功。
4、如果当前位置的 hashcode == MOVED == -1,则需要进行扩容。
5、如果都不满足,则利用 synchronized 锁写入数据。
6、如果数量大于 TREEIFY_THRESHOLD 则要转换为红黑树。
1 final V putVal(K key, V value, boolean onlyIfAbsent) { 2 if (key == null || value == null) throw new NullPointerException(); 3 int hash = spread(key.hashCode()); 4 int binCount = 0; 5 for (Node<K,V>[] tab = table;;) {//1 6 Node<K,V> f; int n, i, fh; 7 if (tab == null || (n = tab.length) == 0)//2 8 tab = initTable(); 9 else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {//3 10 if (casTabAt(tab, i, null, 11 new Node<K,V>(hash, key, value, null))) 12 break; // no lock when adding to empty bin 13 } 14 else if ((fh = f.hash) == MOVED)//4 15 tab = helpTransfer(tab, f); 16 else { 17 V oldVal = null; 18 synchronized (f) {//5 19 if (tabAt(tab, i) == f) { 20 if (fh >= 0) { 21 binCount = 1; 22 for (Node<K,V> e = f;; ++binCount) { 23 K ek; 24 if (e.hash == hash && 25 ((ek = e.key) == key || 26 (ek != null && key.equals(ek)))) { 27 oldVal = e.val; 28 if (!onlyIfAbsent) 29 e.val = value; 30 break; 31 } 32 Node<K,V> pred = e; 33 if ((e = e.next) == null) { 34 pred.next = new Node<K,V>(hash, key, 35 value, null); 36 break; 37 } 38 } 39 } 40 else if (f instanceof TreeBin) { 41 Node<K,V> p; 42 binCount = 2; 43 if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key, 44 value)) != null) { 45 oldVal = p.val; 46 if (!onlyIfAbsent) 47 p.val = value; 48 } 49 } 50 } 51 } 52 if (binCount != 0) { 53 if (binCount >= TREEIFY_THRESHOLD)//6 54 treeifyBin(tab, i); 55 if (oldVal != null) 56 return oldVal; 57 break; 58 } 59 } 60 } 61 addCount(1L, binCount); 62 return null; 63 }
可以看到1.8取消了1.7中使用的ReentrantLock改为了synchronized,也看出1.8jdk对synchronized优化后的信心。
作者:hangwei
出处:http://www.cnblogs.com/hangwei/
关于作者:专注于开源平台,分布式系统的架构设计与开发、数据库性能调优等工作。如有问题或建议,请多多赐教!
版权声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。
如果您觉得文章对您有帮助,可以点击文章右下角“推荐”一下。您的鼓励是作者坚持原创和持续写作的最大动力!