ConcurrentHashMap是J.U.C包下一个十分重要的并发容器之一,它和Hashtable一样是并发安全的,但是它的性能却比Hashtable好很多,且由于在高并发环境下,使用HashMap的put方法时会造成堵塞,这将会消耗许多的CPU时间片,造成不必要的等待,所以在并发情况下应当使用ConcurrentHashMap替代HashMap
1.1 JDK 5中ConcurrentHashMap的实现原理
早期的ConcurrenntHashMap通过分段锁Segment和HashEntry细化了锁的范围,实现了并发安全,由于不像Hashtable一样使用全局锁, 而是细分锁的数据规模,所以性能上比同步的Hashtable高了许多,详细了解可以阅读这篇文章:
https://www.cnblogs.com/ITtangtang/p/3948786.html

1.2 JDK 8中ConcurrentHashMap的实现原理
原理:使用CAS技术与同步锁技术相结合
1.不允许插入null键或null值
2.putAll源码中,不直接使用Java层次级别的数组更新,而是使用CAS的方式直接从系统底层内存空间中更新,所以并发安全
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) {
//通过cas进行添加
if (casTabAt(tab, i, null,
new Node<K,V>(hash, key, value, null)))
break;
}
//....
1.3 CAS基本原理
CAS 全称是 compare and swap,即比较和交换,是一种用于在多线程环境下实现同步功能的机制。CAS 操作包含三个操作数: 内存位置、预期数值和新值。CAS 的实现逻辑是将内存位置处的数值与预期数值相比较,若相等,则说明没有其他线程对其进行过修改,将内存位置处的值替换为新值。若不相等,则说明有其他线程对其进行了修改,不做任何操作,即原地自旋等待
在 Java 中,Java 并没有直接实现 CAS,CAS 相关的实现是通过 C++ 内联汇编的形式实现的。Java 代码需通过 JNI 才能调用。
像synchronized这种独占锁属于悲观锁,它是在假设一定会发生冲突的,那么加锁恰好有用,除此之外,还有乐观锁,乐观锁的含义就是假设没有发生冲突,那么我正好可以进行某项操作,如果要是发生冲突呢,那就重试直到成功,乐观锁最常见的就是CAS。
1.4 ABA问题
CAS 由三个步骤组成,分别是“读取->比较->写回”。考虑这样一种情况,线程1和线程2同时执行 CAS 逻辑,两个线程的执行顺序如下:
时刻1:线程1执行读取操作,获取原值 A,然后线程被切换走
时刻2:线程2执行完成 CAS 操作将原值由 A 修改为 B
时刻3:线程2再次执行 CAS 操作,并将原值由 B 修改为 A
时刻4:线程1恢复运行,将比较值与原值进行比较,发现两个值相等。然后用新值写入内存中,完成 CAS 操作
如上流程,线程1并不知道原值已经被修改过了,在它看来并没什么变化,所以它会继续往下执行流程。
对于 ABA 问题,通常的处理措施是对每一次 CAS 操作设置版本号。J.U.C包下提供了一个可处理 ABA 问题的原子类 AtomicStampedReferenc,具体可以查阅资料了解
ConcurrentHashMap和CAS就先简单地了解到这里吧…查阅其他资料看到的都是cpp和汇编的解读,看得一头雾水,我太难了,就先这样吧(゜-゜)つロ (干杯)

本文详细解析了JDK5和JDK8中ConcurrentHashMap的实现原理,包括分段锁和CAS技术的应用,以及ABA问题的解决方案。探讨了在高并发环境下,ConcurrentHashMap如何通过细分锁的数据规模和使用CAS技术提升性能。
1882

被折叠的 条评论
为什么被折叠?



