ConcurrentHashMap的get操作的步骤如下:
- 通过key计算hash值
- 如果通过hash值判断key对应的节点是否在Node数组中,如果在则返回对应的值。此处分为3种情况:
- 如果恰好是数组元素,也即没有hash值计算得到的桶内只有一个节点,返回改节点的值;
- 如果是红黑树,则通过树的遍历方式去获取,然后返回值;
- 如果是普通链表,则通过链表的方式去获取。
- 如果通过hash值计算定位到的桶位置上没有元素,则返回null。
注意:ConcurrentHashMap的get操作并没有像put操作一样有CAS和synchronized锁。get操作不需要加锁,因为 Node 的元素 value 和指针 next 是用 volatile 修饰的,所以在多线程的环境下,即便value的值被修改了,在线程之间也是可见的。
public V get(Object key) {
Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
// 根据key计算hashcode
int h = spread(key.hashCode());
// key对应的节点在数组内
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;
}
// 这一块需要注意
// 当hash值为负值的时候,表明正在进行扩容操作,这个时候查的是ForwardingNode的find方法来定位到nextTable
// eh=-1,说明该节点是一个ForwardingNode,正在迁移,这时候调用ForwardingNode的find方法去nextTable里找
// eh=-2,说明该节点是一个TreeBin,也就是红黑树节点,这时候会调用TreeBin的find方法遍历红黑树,由于红黑树有可能正在旋转变色,所以find里会有读写锁
// static final int MOVED = -1; // hash for forwarding nodes
// static final int TREEBIN = -2; // hash for roots of trees
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;
}
}
// key对应的节点不在数组内则返回null
return null;
}
ForwardingNode.find方法
Node<K,V> find(int h, Object k) {
// loop to avoid arbitrarily deep recursion on forwarding nodes
outer: for (Node<K,V>[] tab = nextTable;;) {
Node<K,V> e; int n;
if (k == null || tab == null || (n = tab.length) == 0 ||
(e = tabAt(tab, (n - 1) & h)) == null)
return null;
for (;;) {
int eh; K ek;
if ((eh = e.hash) == h &&
((ek = e.key) == k || (ek != null && k.equals(ek))))
return e;
if (eh < 0) {
if (e instanceof ForwardingNode) {
tab = ((ForwardingNode<K,V>)e).nextTable;
continue outer;
}
else
return e.find(h, k);
}
if ((e = e.next) == null)
return null;
}
}
}
TreeBin.find方法
final Node<K,V> find(int h, Object k) {
if (k != null) {
for (Node<K,V> e = first; e != null; ) {
int s; K ek;
if (((s = lockState) & (WAITER|WRITER)) != 0) {
if (e.hash == h &&
((ek = e.key) == k || (ek != null && k.equals(ek))))
return e;
e = e.next;
}
else if (U.compareAndSwapInt(this, LOCKSTATE, s,
s + READER)) {
TreeNode<K,V> r, p;
try {
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;
}