ConcurrentHashMap详解
本文旨在从源码层面分析ConsurrentHashMap的原理
为什么说ConcurrentHashMap是线程安全的呢
- 为什么会有线程安全问题
因为多线程情况下操作同一个资源,因为操作不是原子性的,导致出现脏写等情况
拿HashMap举例来说,下图是他的put方法,逻辑是我们常用的,先判断元素是否存在,不存在就设置值
- ConcurrentHashMap是怎么解决的
首先put方法是一个for无限循环,完成后才会退出
当put数据时,计算出hashmod值后- 底层数组该位置没有数据
使用cas的方式插入数据,如果成功,则直接返回,如果失败,继续外层循环,使用乐观锁 + CAS的方式,保证线程安全 - 当底层数组该位置已经有数据
对该位置链表的头部元素加锁
- 底层数组该位置没有数据
ConcurrentHashMap的get方法有加锁吗,为什么
从源码看,当key不存在或者value所在的数组位置为链表时,没有加锁,但是当value所在的数组位置为tree时,会执行find方法
public V get(Object key) {
ConcurrentHashMap.Node<K,V>[] tab; ConcurrentHashMap.Node<K,V> e, p; int n, eh; K ek;
int h = spread(key.hashCode());
if ((tab = table) != null && (n = tab.length) > 0 &&
(e = tabAt(tab, (n - 1) & h)) != null) {
if ((eh = e.hash) == h) {
if ((ek = e.key) == key || (ek != null && key.equals(ek)))
return e.val;
}
else if (eh < 0)
return (p = e.find(h, key)) != null ? p.val : null;
while ((e = e.next) != null) {
if (e.hash == h &&
((ek = e.key) == key || (ek != null && key.equals(ek))))
return e.val;
}
}
return null;
}
find方法
/**
* Returns matching node or null if none. Tries to search
* using tree comparisons from root, but continues linear
* search when lock not available.
*/
final Node<K,V> find(int h, Object k) {
if (k != null) {
for (Node<K,V> e = first; e != null; ) {
int s; K ek;
// 如果当前读写锁的状态是WAITER或者WRITER,则通过链表遍历查找,此时红黑树正在调整的过程中。
if (((s = lockState) & (WAITER|WRITER)) != 0) {
if (e.hash == h &&
((ek = e.key) == k || (ek != null && k.equals(ek))))
return e;
e = e.next;
}
// //如果不是上述两种状态,则将状态设置为READER,每次都会加上READER,表示正在遍历红黑树,此时就不能调整红黑树了
else if (U.compareAndSwapInt(this, LOCKSTATE, s,
s + READER)) {
TreeNode<K,V> r, p;
try {
// 如果根节点为null,则返回null;否则通过根节点查找匹配的节点。注意:进入此方法根节点不可能为null
p = ((r = root) == null ? null :
r.findTreeNode(h, k, null));
} finally {
Thread w;
if (U.getAndAddInt(this, LOCKSTATE, -READER) == //所有的读锁都释放了
(READER|WAITER) && (w = waiter) != null) //且有等待获取写锁的线程
// 唤醒该线程
LockSupport.unpark(w);
}
return p;
}
}
}
return null;
}
ConcurrentHashMap迭代器是强一致还是弱一致
是弱一致的,因为在迭代过程中,ConcurrentHashMap没有加锁,可以插入数据,而HashMap不能插入数据
// HashMap
final Node<K,V> nextNode() {
Node<K,V>[] t;
Node<K,V> e = next;
// 元素个数不对,直接抛出错误
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
if (e == null)
throw new NoSuchElementException();
if ((next = (current = e).next) == null && (t = table) != null) {
do {} while (index < t.length && (next = t[index++]) == null);
}
return e;
}