上一篇 <<<JDK1.8HashMap源码分析
下一篇 >>>
ConcurrentHashMap特点:
a、支持多线程效率高,默认都分成16个Segments
b、不止key为null
Jdk1.7版本和1.8版本的ConcurrentHashMap有何区别
Jdk1.7版本的ConcurrentHashMap采用分段锁;
1.底层采用分段锁机制,分成16个不同的Segment,每个Segment独立table有独立是锁, Segment其实就是我们的hashTable
Segment底层继承ReentrantLock上锁,使用CAS做修改,最大支持64次自旋
2.根据key计算index存放在Segment位置;
ConcurrentHashMap → Segments(16 个HashTable) → HashEntry
3.index计算两次
缺点:
a、挺占内存的
b、每个Segment有独立的锁,也会存在等待问题。
c、需要计算两次index值:第一次计算存放那个Segment对象中 ,第二次计算Segment对象中那个HashEntry<K,V>[] table
优点:扩容的时候,是每个Segment独立运行,不存在冲突问题,解决了hashmap的死循环问题
ConcurrentHashMap 1.7 使用Segment+ReentrantLock
ConcurrentHashMap 实际上是HashTable的扩展,调用使用Segment,真正底部是用HashEntry<K,V>单向链表数组
代码情况:
class Segment<K,V> extends ReentrantLock,其中Segment中都有HashEntry<K,V>[] table;
HashEntry属性:
final int hash;
final K key;
volatile V val;
volatile HashEntry<K,V> next;
1、初始化
默认容量:16
负载因子:0.75f
2、put方法
a、如果传入的是空,则抛出异常
b、计算hash值
int hash = hash(key);
c、根据hash判断存放在哪个Segment中
int j = (hash >>> segmentShift) & segmentMask;
s = ensureSegment(j);
c、添加节点 s.put(key, hash, value, false);
c1、上锁ReentrantLock tryLock()
c2、加数据
--如果达到阈值,则扩容:newCapacity = oldCapacity << 1; 2倍方式扩容,扩容后下标重新计算
--扩容只是针对单个Segment的HashEntry,不影响整体的大小,解决了hashmap的死循环问题
c3、释放锁ReentrantLock
Jdk1.8版本的ConcurrentHashMap删除了分段锁技术 ,改用CAS无锁机制+synchronized机制。
1.New Node 采用cas 乐观锁机制保证线程安全性的问题;
2.如果计算index(只计算一次)产生了冲突 ,使用synchronized上锁;
--实际上是直接在节点上上锁。
ConcurrentHashMap 1.8 使用CAS+Synchronized
CAS是乐观锁,通过自旋,不会造成阻塞。
hashmap在下面语句的时候可能会存在线程安全问题
tab[i] = newNode(hash, key, value, null);
concurrentmap如何解决线程安全问题:
1.在创建对象的时候加上了CAS锁
if (casTabAt(tab, i, null,new Node<K,V>(hash, key, value, null)))
casTabAt上锁实际方法:U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
2.如果index产生了冲突,则使用同步锁
synchronized (f) {}
ConcurrentHashMap 实际上是HashTable的扩展,底层使用Node单向链表数组
属性:
final int hash;
final K key;
volatile V val;
volatile Node<K,V> next;
默认容量16
比较项目 | JDK1.7的ConcurrentHashMap | JDK1.8的ConcurrentHashMap |
---|---|---|
数据结构 | 数组+Segments分段锁+HashEntry链表 | 数组+链表+红黑树(使用Node数组保存数据) |
锁的实现 | Lock锁+CAS乐观锁+Unsafe类 | 取消Segment分段锁设计,使用CAS+Synchronized保证并发风险 |
扩容实现 | 支持多个Segment同时扩容 | 支持并发扩容 |
ConcurrentHashMap 1.8 为什么要使用CAS+Synchronized取代Segment+ReentrantLock
Segment+ReentrantLock锁住的是单个Segment,也就是单个HashTable,但CAS+Synchronized锁住的是节点,锁的粒度比jdk1.7更加精细;而且JDK1.8的Synchronized性能上也大大的提升了。
网友评论