目录
https://blog.csdn.net/e5yrt2/article/details/105249839之前这篇文章介绍了hashmap的源码,但是hashmap是线程不安全的,jdk同样提供了java.util.concurrent包下的ConcurrentHashMap来解决这个问题
jdk1.7中的实现
本文虽然主要是讲1.8中的源码实现逻辑,但是提一下1.7中的实现逻辑也方便更好的理解。
jdk1.7主要采用了分离锁的思想实现的,也就是将内部进行分段(Segment),里面则是 HashEntry 的数组,和 HashMap 类似,哈希相同的条目也是以链表形式存放,而Segment又是ReentrantLock的一种实现。
static final class Segment<K,V> extends ReentrantLock implements Serializable {
private static final long serialVersionUID = 2249069246763182397L;
//自旋锁最多尝试次数
static final int MAX_SCAN_RETRIES =
Runtime.getRuntime().availableProcessors() > 1 ? 64 : 1;
//作用同HashMap中的HashEntry一样
transient volatile HashEntry<K,V>[] table;
transient int count;
transient int modCount;
transient int threshold;
final float loadFactor;
}
final Segment<K,V>[] segments;
jdk1.8中的实现
jdk1.8中主要采用了CAS和synchronized来保证并发安全性。而整个结构与1.8中的HashMap区别不大,我这里主要说下如何实现并发安全的
put方法
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; K fk; V fv;
if (tab == null || (n = tab.length) == 0)
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
// 利用CAS去进行无锁线程安全操作,如果bin是空的
if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value)))
break;
}
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else if (onlyIfAbsent // 不加锁,进行检查
&& fh == hash
&& ((fk = f.key) == key || (fk != null && key.equals(fk)))
&& (fv = f.val) != null)
return fv;
else {
V oldVal = null;
synchronized (f) {
// 细粒度的同步修改操作...
}
}
// Bin超过阈值,进行树化
if (binCount != 0) {
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
if (oldVal != null)
return oldVal;
break;
}
}
}
addCount(1L, binCount);
return null;
}
主要实现的步骤如下:
①使用spread方法计算出hash值
②判断tab是否为空决定是否需要初始化
③定位Node,如为空则利用CAS写入数据
④不满足上述则利用synchronized写入数据