浅谈HashMap线程为何不安全

2017-12-22  本文已影响0人  值得一看的喵

如果多个线程同时访问这个Map,而且至少一个线程对Map进行结构性的修改(增加,删除操作,update不算),那么它必须在外部进行同步。比如一个线程对HashMap进行扩容,另外一个线程读取HashMap的值,扩容可能导致数组table的length变了。 

比如,key原来对应2的位置,扩容后变成了18位置,相当于数组长度从16变成了32。 

而一个线程在扩容之前取得了数组length是16,而在table[index]的时候刚好扩容完成,还是从2的位置读取元素,那肯定读出来的是null,而实际上HashMap中是有值的。 

这样的数据错误,是由于线程不安全造成的。 

而在两个线程同时写HashMap时候的数据不一致,这个只能从业务层做保证了,肯定会不一致,就算是ConcurrentHashMap也会产生不一致。

为什么ConcurrentHashMap可以做到在扩容的时候读取正确的值呢?

ConcurrentHashMap中在扩容的时候,会复制原来数组的元素table到nextTable,复制过程会对原有的链表头结点加sychronize。

在每个节点链表复制完成之后,把原有节点变成forwardingNode,指向nextTable。

这样,在复制过程中,get函数依然能获取正确的值: 

1、该数组节点没有被复制完,那么则会查找Node类型的链表头结点 

2、该数组节点已经被复制完,那么则会查找nextTable中的元素

如果该数组节点没有被复制完,且没有sychronized,那么说明还没有复制到这个节点,(因为节点复制是从数组下标为0开始的,逐个递增)那么可以在该节点的链表进行插入,插入需要加sychronized。

如果该数组节点没有复制完,且有sychronized,那么插入操作只能等待,直到获得锁。等该节点变成forwardingNode,就能获得锁,进行插入。这时插入操作会插入到nextTable中了。

如果该数组节点已经被复制完,那么是forwardingNode类型,需要插入到nextTable中了。

上一篇 下一篇

猜你喜欢

热点阅读