落叶知秋|ConcurrentHashMap确实很复杂,这样学源码才简单( 三 )


还是和上面的一样 , HashTable就不说了 , putVal()方法被synchronized了 , 里面所有的操作都是线程安全的 , Collections.synchronizedMap()类似 。
我们来看看在ConcurrentHashMap中是怎么来保证线程安全的 , 还是找到putVal()方法:
落叶知秋|ConcurrentHashMap确实很复杂,这样学源码才简单casTabAt()
看上面的源码注释 , 当数组被安全的的初始化后 , 就开始put值进去了 , 这个地方一个是通过U.getObjectVolatile()来线程安全的判断出该数组下标对应的节点是否为null , 如果是null , 则执行casTabAt()方法去写入!
还是采用的是CAS无锁化的技术来保证写入的线程安全!
5、ConcurrentHashMap除了CAS还有其他保证线程安全的方式吗?上面两部分说的都是ConcurrentHashMap中用CAS来保证数组的初始化、下标为空的时候putVal()时候的线程安全 。
那是不是ConcurrentHashMap中是不是都是用CAS来保证线程安全的呢?
还有其他的方式吗?synchronized出现了!
也就是说初始化数组、数组下标为null时候采用的都是CAS操作来保证线程安全的 , 但是当当前下标不为空的时候 , 就采用synchronized来保证线程安全了:
落叶知秋|ConcurrentHashMap确实很复杂,这样学源码才简单那么问题来了:

  1. 不是说synchronized慢么?这里是怎么回事?
  2. 为什么不用CAS?
这里的synchronized锁的是对象f , 这个f有可能就是一个Node , 有可能是一个链表或红黑树 , 即:
f = tabAt(tab, i = (n - 1) 这个方法主要分成三部分:
  1. 每个线程领取搬运元素的任务 , 每次协助搬运数组里的16个下标对应的元素;
  2. 不断的去检查每一个线程的任务是否完成 , 全部都完成了才返回;
  3. 每个线程去做任务搬运数据到新的数组 。
对应的三块源码为:
落叶知秋|ConcurrentHashMap确实很复杂,这样学源码才简单
落叶知秋|ConcurrentHashMap确实很复杂,这样学源码才简单
落叶知秋|ConcurrentHashMap确实很复杂,这样学源码才简单7、总结如果你已经看到这里了 , 我不甚感激 , 当然你也一定有所收获!
回过头提炼一下这些知识点 , 不关心里面的代码实现细节 , 其实就剩下最后一个问题:
面试官问你ConcurrentHashMap真正要考察的知识是什么?
如果还是考察你数组+链表+红黑树 , 那就问你HashMap就好了 。 问ConcurrentHashMap的本质是想带你进入并发编程的世界(坑) , 看看你对并发编程的掌握程度如何 。
如果你去看ConcurrentHashMap的源码 , 你会发现到处都是CAS、synchronized、volatile这些并发编程的相关知识 , ConcurrentHashMap就是靠这些东西来保证其线程安全的特性 。
【落叶知秋|ConcurrentHashMap确实很复杂,这样学源码才简单】因此 , 带着对ConcurrentHashMap的了解 , 开始进入并发编程的世界吧~


推荐阅读