ConcurrentHashMap,非常体现水平的面试题
8、Concurrenthashmap
1.7版本:用segment对象 segment:小型的由链表组成的hashmap(数组)
当键值对进来就进行扩容, 转移到新数组。
1.8版本:不用segment对象 只有一个数组,直接进行扩容。1.8特性多线程扩容每一个线程同时负责不同位置的元素。
在创立一个concurrenthashmap时,如果需要指定长度可以如下:
ConcurrentHashmap chm=new ConcurrentHashmap(32);
但是在1.7环境下长度就是32,而1.8源代码如下 相当于会从原长度+原长度左移一位(一半)在+1。而长度会固定为2的次幂,所以实际长度会增加一倍 为64。
编辑
此值被记录成sizeCtl
编辑
何时判断数组初始化:需看put方法源码可知
编辑
而这个initTable就是初始化的标志,所以调用put方法时就是数组初始化。而一但进行扩容时我们需要考虑到并发问题,而将sizeCtl改为-1就代表有数组正在初始化,这样就能解决并发问题(实在是妙,一个变量这么多用处)源代码实现如下:
编辑
就这一段代码,可以体现出作者的智慧与细节。完美地解释了sizeCtl四种值的情况。希望大家能通过自己的摸索来理解这一段代码,这样你的java水平会提高一个层次。这段代码有助于我们记忆sizeCtl的用处。
我们知道,当hashmap元素个数达到8个时会树化,这是一定的吗?
编辑
编辑
源码可知,确实达到8个元素时会树化,但是再继续看如下源码:
编辑编辑
这个treeifyBin方法显示了,如果链表长度小于这个参数(64)就不会进行树化,所以这也是一个细节点(面试会问!)。继续看这段代码,我们可以看到他加上了synchronized关键字,而他封装的对象是这个节点或者说就是这个桶,这又是一个考点。因为在HashTable中,我们加锁是整个数组加锁,而这里是对单个节点加锁,这又是一个区别所在。同时这也是为何他是Concurrent(并发),保证安全的一个操作。如果数组长度不够树化,他会进行一个扩容,清看tryPresize方法源码:编辑
可知,会扩容成两倍(跟之前长度为2的次幂的知识遥相呼应)。
其实内部还有更多的东西需要探讨,目前了解到这里可以应付大部分面试,之后会根据需求来决定是否进行内容扩充。
#秋招以来你最大的收获是什么#