1.hashmap数据结构?是线程安全吗?为什么不是线程安全?1.8为什么用黑红树?1.8为什么大于8使用红黑树?和avl树比有什么优缺点?
2.ConcurrentHashMap数据结构?怎样实现线程安全的?jdk1.8比1.7有什么改变?get为什么不加锁?可以实现无锁吗?迭代器是强一致还是弱一致?
以上是我面试中常见的问题,我试着自我总结下
答:1.jdk1.7数据结构数组+链表,jdk1.8数组+链表当链表的长度大于包括8转换红黑树。hashmap非线程安全。为什么非线程安全:1)多线程put时,若hashcode相等,表头无数据,会覆盖数据。2)多线程put,remove时,有可能put数据存储到需要remove数据之后,导致数据丢失 3)扩容时,多个线程扩容很有可能将前面已经扩容完成的数据覆盖,导致数据丢失等等。1.8为什么用红黑树,为了提高检索和put时间,1.7 put和get数据需要遍历链表上的数据意味着时间复杂度o(n),采用了红黑树时间复杂度o(lgn)。大于8个元素时采用黑红树,我看了下源码注释,不一定对1).泊松离散分布离散率达到8概率比较小注释在143行 2).TreeNodes的大小大约是Node的两倍注释也是在143行开始。和avl树优缺点自行谷歌。
答:2.ConcurrentHashMap同hashmap数据结构一致。1.7中采用Segment + HashEntry的方式进行实现,Segment默认16意味着16个并发。1.8锁住了表头,相比1.7降低了锁的粒度,同时代码复杂度增加(1.8ConcurrentHashMap 6000多行 看的懵逼)。get不加锁的原因是因为volatile,1.7和1.8都使用了UNSAFE的getObjectVolatile具有读的volatile语义,然后可以延伸volatile的实现和作用,这里不过多提。ConcurrentHashMap可以实现无锁的实现比如jctools的NonBlockingHashMap,netty就用到了jctools,netty也大量运用到了volatile,cas等很好的并发实战框架,NonBlockingHashMap采用的是无锁的线性探测(多线程这块太复杂这些map搞的头大,也看不懂),采用无锁,实现复杂度可能会更高。ConcurrentHashMap迭代器弱一致的,若是提供强一致更需要复杂的逻辑,具体看源码吧。
关于Elasticsearch bug号 #18060 也是关于ConcurrentHashMap但是我始终没明白原因 ,也是ConcurrentHashMap没看明白的原因。希望有大佬带带我。