ConurrentHashMap源码分析
推荐阅读文章:Map大家族的那点事儿
在jdk1.8中ConcurrentHashMap所采用的线程安全技术有:
1.volatile 关键字 (核心)
/**
* The array of bins. Lazily initialized upon first insertion.
* Size is always a power of two. Accessed directly by iterators.
*/
transient volatile Node<K,V>[] table;
2.CAS技术 (核心)
static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,Node<K,V> c, Node<K,V> v) {
return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
}
3.synchronized 关键字 (次要、细粒度锁、且少量出现)
final V putVal(K key, V value, boolean onlyIfAbsent) {
....
synchronized (f) {
....
}
....
}
初始化
通过cas与sizeCtl 变量的相互配合,来保证初始化时的线程安全
/**
* Table initialization and resizing control. When negative, the
* table is being initialized or resized: -1 for initialization,
* else -(1 + the number of active resizing threads). Otherwise,
* when table is null, holds the initial table size to use upon
* creation, or 0 for default. After initialization, holds the
* next element count value upon which to resize the table.
*/
private transient volatile int sizeCtl;
/**
* Initializes table, using the size recorded in sizeCtl.
*/
private final Node<K,V>[] initTable() {
Node<K,V>[] tab; int sc;
while ((tab = table) == null || tab.length == 0) {
// sizeCtl小于0,这意味着已经有其他线程进行初始化了
// 所以当前线程让出CPU时间片
if ((sc = sizeCtl) < 0)
Thread.yield(); // lost initialization race; just spin
// 否则,通过CAS操作尝试修改sizeCtl
else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
try {
if ((tab = table) == null || tab.length == 0) {
// 默认构造函数,sizeCtl = 0,使用默认容量(16)进行初始化
// 否则,会根据sizeCtl进行初始化
int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
@SuppressWarnings("unchecked")
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
table = tab = nt;
// 计算阈值,n的75%
sc = n - (n >>> 2);
}
} finally {
// 阈值赋给sizeCtl
sizeCtl = sc;
}
break;
}
}
return tab;
}
get
get方法并未加锁,由于volatile关键的修饰,获取到的值一定是最新值,不存在线程安全问题。
/**
* The array of bins. Lazily initialized upon first insertion.
* Size is always a power of two. Accessed directly by iterators.
*/
transient volatile Node<K,V>[] table;
put
在put操作过程中涉及扩容与转树问题,当达到预设的变树阙值时,ConcurrentHashMap将由原先的散列链表结构变成散列红黑树结构
变树与扩容的过程中通过局部synchronized 来保证线程安全
/**
* Replaces all linked nodes in bin at given index unless table is
* too small, in which case resizes instead.
*/
//变树过程
private final void treeifyBin(Node<K,V>[] tab, int index) {
Node<K,V> b; int n, sc;
if (tab != null) {
if ((n = tab.length) < MIN_TREEIFY_CAPACITY)
tryPresize(n << 1);
else if ((b = tabAt(tab, index)) != null && b.hash >= 0) {
synchronized (b) {
if (tabAt(tab, index) == b) {
TreeNode<K,V> hd = null, tl = null;
for (Node<K,V> e = b; e != null; e = e.next) {
TreeNode<K,V> p =
new TreeNode<K,V>(e.hash, e.key, e.val,
null, null);
if ((p.prev = tl) == null)
hd = p;
else
tl.next = p;
tl = p;
}
setTabAt(tab, index, new TreeBin<K,V>(hd));
}
}
}
}
}
size
Java 8声明了一个volatile变量baseCount用于记录元素的个数,对这个变量的修改操作是基于CAS的,每当插入元素或删除元素时都会调用addCount()函数进行计数