总结:
分段机制:segment,每段加reentrantLock可重入锁
定位元素:1 找segment数组下标 2 找segment的HashEntry数组下标
get方法:不需要加锁,value值使用了volatile关键字修饰
put方法:hash计算段---锁定段---hash计算HashEntry数组---若超多阈值---扩容---释放;put过程中会modCount+1,为了后续的计算大小
size()方法:为了求Map的大小,需要全局锁,但是性能差;于是采用modCount计算器,用于记录段的大小;
ConcurrentHashMap的弱一致性:clear时,是不加锁的,所以多线程中,可能出现数据不一致;
因为没有全局的锁,在清除完一个segments之后,正在清理下一个segments的时候,已经清理segments可能又被加入了数据,因此clear返回的时候,ConcurrentHashMap中是可能存在数据的。因此,clear方法是弱一致的。
ConcurrentHashMap的弱一致性主要是为了提升效率,是一致性与效率之间的一种权衡。要成为强一致性,就得到处使用锁,甚至是全局锁,这就与Hashtable和同步的HashMap一样了。
而在迭代时,ConcurrentHashMap使用了不同于传统集合的快速失败迭代器(见之前的文章《JAVA API备忘---集合》)的另一种迭代方式,我们称为弱一致迭代器。在这种迭代方式中,当iterator被创建后集合再发生改变就不再是抛出ConcurrentModificationException,取而代之的是在改变时new新的数据从而不影响原有的数据,iterator完成后再将头指针替换为新的数据,这样iterator线程可以使用原来老的数据,而写线程也可以并发的完成改变,更重要的,这保证了多个线程并发执行的连续性和扩展性,是性能提升的关键。
详情:
ConcurrentHashMap基本思想:
ConcurrentHashMap数据结构
ConcurrentHashMap的初始化
ConcurrentHashMap的get方法
ConcurrentHashMap的put方法
ConcurrentHashMap的size方法
ConcurrentHashMap的弱一致性
因为没有全局的锁,在清除完一个segments之后,正在清理下一个segments的时候,已经清理segments可能又被加入了数据,因此clear返回的时候,ConcurrentHashMap中是可能存在数据的。因此,clear方法是弱一致的。
举2个比较简单的例子就可以看出ConcurrentHashMap的弱一致性。
第一个,就是clear方法。假设线程1执行到Segment[0].clear()的时候,Segment[0]中的元素元素已经被clear,但是线程2此时可以在Segment[0]中增加元素(clear不需要加锁),这样线程1执行结束的时候就ConcurrentHashMap并不是空的。
第二个例子,判断key是否存在,不存在则进行put。假设线程1判断key不存在后挂起,线程2判断key也不存在,然后线程2执行put操作,之后线程1执行put操作,此时线程2读取到的会是线程1设置的值而不是线程2设置的值。当然此时应该是应用putIfAbsent方法。
小结
ConcurrentHashMap在设计思路对效率和一致性上的平衡以及一些lock-free的方法非常值得借鉴,同时注意虽然类本身是线程安全的,但是不要认为使用该类就一定是线程安全的。