【图解】面试题:ConcurrentHashMap是如何保证线程安全的
注意:JDK1.7与JDK1.8中的ConcurrentHashMap主要延续HashMap的设计与思想,是在其基础上进行的相应优化
1.JDK1.7中的底层实现原理
(1)JDK1.7ConcurrentHashMap的底层结构,延续HashMap的底层设计,采用数组+链表
(2)不同的是:ConcurrentHashMap中的数组被分为大数组和小数组
,大数组是Segment
,小数组是HashEntry
Segment本身是基于ReentrantLock可重入锁来实现加锁和释放锁,这样就能保证多线程同时访问ConcurrentHashMap的时候,同一时间只能有一个线程操作对应的节点,以此来保证线程安全
总结:这里基于Segment加锁和释放锁,因此称之为分段锁或分片锁
2.JDK1.8中的底层实现原理
(1)JDK1.8的ConcurrentHashMap底层采用的是数组+链表+红黑树,这里对JDK1.7进行了优化,在链表长度大于8并且数组长度大于64时链表就会转化为红黑树
(2)JDK1.8中保留了Segment的定义,这仅仅是为了保证序列化时的兼容性,不再有任何结构上的用途
(3)JDK1.8中主要使用volatile+CAS或者是synchronized的方法来实现的,以此来保证线程安全
(i)源码中,添加元素时,首先会判断容器是否为空,为空,就会使用volatile加CAS来初始化
(ii)容器不为空,就会根据存储的元素计算该位置是否为空,如果计算结果位置的结果为空,就会使用CAS来设计该节点,不为空,就会使用synchronized来加锁实现,然后遍历桶中的数据并且替换或者新增节点到桶中,最后判断是否需要转换红黑树
总结:JDK1.8中ConcurrentHashMap是通过对头节点加锁来保证线程安全,降低锁粒度,发生hash冲突和加锁的频率也更低了
附上JDK1.8源码
/**
* Maps the specified key to the specified value in this table.
* Neither the key nor the value can be null.
*
* <p>The value can be retrieved by calling the {@code get} method
* with a key that is equal to the original key.
*
* @param key key with which the specified value is to be associated
* @param value value to be associated with the specified key
* @return the previous value associated with {@code key}, or
* {@code null} if there was no mapping for {@code key}
* @throws NullPointerException if the specified key or value is null
*/
public V put(K key, V value) {
return putVal(key, value, false);
}
/** Implementation for put and putIfAbsent */
final V putVal(K key, V value, boolean onlyIfAbsent) {
if (key == null || value == null) throw new NullPointerException();
int hash = spread(key.hashCode());
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
tab = initTable();
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
}
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
V oldVal = null;
synchronized (f) {
if (tabAt(tab, i) == f) {
if (fh >= 0) {
binCount = 1;
for (Node<K,V> e = f;; ++binCount) {
K ek;
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;
}
}
}
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;
}
}
}
}
if (binCount != 0) {
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
if (oldVal != null)
return oldVal;
break;
}
}
}
addCount(1L, binCount);
return null;
}
最新文章
- 安卓真机调试 出现Installation error: INSTALL_FAILED_UPDATE_INCOMPATIBLE....
- 解决OracleConnection ORA-1017 和 HRESULT:0x8007000B 错误
- java开发环境
- Node.js高效按行输出文件内容
- JSP公用COMMON文件
- 日期运算 jsf日期组建
- windows 8 设置hyper-v网络设置
- LeetCode Find Minimum in Rotated Sorted Array II
- [python]倒计时实现
- BZOJ 1589: [Usaco2008 Dec]Trick or Treat on the Farm 采集糖果
- 一些提高开发效率的 Category
- 数据可视化开源系统(python开发)
- perl学习(8) 控制:unless,until,next,redo,last
- [转]Blue Prism Architecture
- python,列表,元祖,字典
- Jmeter 登入、新增、查询、修改、删除,动态传参。
- 版本控制git第一篇
- 用Axios Element 实现全局的请求 loading
- 【jsp】配置错误页面
- discuz formhash