ConcurrentHashMap

java.util.concurrent包下的ConcurrentHashMap是fail-safe的,安全失败
而java.util包下的集合类是fail-fast的,快速失败。在使用迭代器的时候,快速失败的集合类是会抛出ConcurrentModificationException异常,而安全失败的迭代器不会。
举例:ArrayList是快速失败的案例,如果在多线程下迭代,迭代器hashNext是判断是否有下一个元素,next用于返回当前元素并把指针指向下一个元素,如果是多线程情况下,利用CAS,会先获取容器的计数器modCount是否与此时相等,如果不相等,说明被修改过,会派出如上异常。

ConcurrentHashMap对put方法的改进:
ConcurrentHashMap底层仍然是散列表+红黑树。
JDK1.7引入了Sement分段锁,这是一个继承了可重入锁的对象锁。
与HashMap的put不同的是,我们在根据key的HashCode找到table中的下标之前,将整个哈希表分为了一个的segement数组,每个segement都占有一部分的table数组+哈希桶,当某一个线程访问某个segment的时候,获得锁,并不影响别的线程访问其它segment,我们只需要在put方法最后,释放这个segment锁即可。

JDK1.8对ConcurrentHashMap进行了脱胎换骨的构造。引入了CAS,弃用了Segment锁。
我们将当前key定位出的Node/Entry使用volatile关键字修饰,保证了可见性以及防止编译器优化造成的指令重排。利用CAS尝试写入,失败则自旋保证成功。因而步骤为:

  1. 根据key计算出hashCode
  2. 定位到table的下标,落槽,找到哈希桶中的位置,然后判断是否可以写入数据,如果为空,尝试利用CAS写入(HashMap是判断如果为空,直接写入的),这样防止了数据丢失,如果哈希槽长度大于8,生长为红黑树。如果当前位置的hashcode==Moved==-1,则需要进行扩容。如果都不满足,则用synchronized锁写入数据(红黑树/链表写入)

get()方法和hashmap没有多大区别:
仍然是根据key计算出来的hashCode寻址(位运算哈希),如果在桶上就直接返回值。
对节点进行判断,如果是树节点,红黑树遍历,如果不是,链表遍历。

总结:

底层结构是:散列表+红黑树,这一点和HashMap是一样的。
Hashtable是将所有的方法使用synchronized锁同步,效率低下,而ConcurrentHashMap是通过只在写入数据的使用使用sucnhronized锁+CAS实现原子性操作来保证线程安全的。CAS算法也可以算是乐观锁的一种。
ConcurrentHashMap的key和value都不能为null。

你可能感兴趣的:(ConcurrentHashMap)