总结:ConcurrentHashMap

一、介绍

HashMap是非线程安全的,而Hashtable虽然是性能安全的,但是性能太低。

ConcurrentHashMap 是Java里面同时兼顾性能和线程安全的一个键值对集合。

HashTable 是一个线程安全的类,因为它的所有 public 方法都被 synchronized 修饰,这样就导致了一个问题,就是效率太低。

二、JDK1.7中是分段锁的思想

基于hash定位segment数组的索引

三、JDK1.8的实现

数据结构采用了和HashMap一样的数据结构,即数组 + 链表或数组 + 红黑树

但是安全方面, ConcurrentHashMap通过CAS去保障

四、ConcurrentHashMap在迭代时和HashMap的区别

当一个集合在迭代的时候如果动态的添加或者删除元素,那么就会抛出 Concurrentmodificationexception ,但是在 ConcurrentHashMap 里面却不会,例如如下代码:

public static void main(String[] args) {
    Map<String,String> map = new ConcurrentHashMap<String, String>();
    map.put("a","a1");
    map.put("b","b1");
    map.put("c","c1");
    map.put("d","d1");
    map.put("e","e1");
    Iterator<String> iterator = map.keySet().iterator();
    while (iterator.hasNext()){
        String it = iterator.next();
        if("b".equals(it)){
            map.remove("d");
        }
        System.out.println(it);
    }
}

控制台打印如下:
a
b
c
e

而当你把 ConcurrentHashMap 换成 HashMap 的时候,控制台就会抛出一个异常:

Exception in thread "main" a
b
java.util.ConcurrentModificationException
    at java.util.HashMap$HashIterator.nextNode(HashMap.java:1442)
    at java.util.HashMap$KeyIterator.next(HashMap.java:1466)
    at xyz.somersames.ListTest.main(ListTest.java:22)

原因在于 ConcurrentHashMap next 方法并不会去检查 modCount expectedModCount ,但是会检查下一个节点是不是为空

  if ((p = next) == null)
    throw new NoSuchElementException();

当我们进行remove的时候, ConcurrentHashMap 会直接通过修改指针的方式来进行移除操作,同样的,也会锁住 数组 的头节点直至移除结束,所以在同一个时刻,只会有一个线程对 当前数组下标的所有节点 进行操作。

但是在 HashMap 里面, next 方法会进行一个check,而remove操作会修改 modCount ,导致 modCount expectedModCount 不相等,所以就会导致
ConcurrentModificationException

稍微修改下代码:

public static void main(String[] args) {
    Map<String,String> map = new ConcurrentHashMap<String, String>();
    map.put("a","a1");
    map.put("b","b1");
    map.put("c","c1");
    map.put("d","d1");
    map.put("e","e1");
    Iterator<String> iterator = map.keySet().iterator();
    while (iterator.hasNext()){
        if("b".equals(iterator.next())){
            map.remove("d");
        }
        System.out.println(iterator.next());
    }
}
控制台打印如下:
b
d
Exception in thread "main" java.util.NoSuchElementException
    at java.util.concurrent.ConcurrentHashMap$KeyIterator.next(ConcurrentHashMap.java:3416)
    at com.xzh.ssmtest.ListTest.main(ListTest.java:25)

并发下的处理

由于每一个 Node 的首节点都会被 synchronized 修饰,从而将一个元素的新增转化为一个原子操作,同时 Node value next 都是由 volatile 关键字进行修饰,从而可以保证可见性。

参考:

ConcurrentHashMap1.7分段锁原理 

JDK1.8下ConcurrentHashMap的一些理解(一) 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
继续介绍 ConcurrentHashMapConcurrentHashMap 的扩容机制: ConcurrentHashMap 采用了分段锁的思想,它将整个数据分为若干个 Segment,每个 Segment 就是一个类似于 HashMap 的结构,它有自己的 Hash 表,它们可以独立的进行扩容和操作,这样就有效的降低了锁的粒度,提高了并发度。 ConcurrentHashMap 的扩容机制与 HashMap 的扩容机制有很大的不同。HashMap 在扩容时会将所有的元素重新计算 Hash 值,然后再重新插入到新的 Hash 表中,这样会造成很大的性能损失。而 ConcurrentHashMap 在扩容时并不会将所有的元素重新计算 Hash 值,而是只将需要扩容的 Segment 进行扩容,这样就大大的降低了扩容的代价。 ConcurrentHashMap 的迭代器: ConcurrentHashMap 提供了两种迭代器:keySet().iterator() 和 entrySet().iterator()。这两种迭代器都是弱一致性的,它们不保证在迭代时能够获取到最新的数据,但是它们保证能够获取到所有的数据,并且不会抛出 ConcurrentModificationException 异常。 ConcurrentHashMap 的弱一致性是通过“快照”机制来实现的,它会在迭代器创建时记录一个“快照”,然后在迭代时使用这个“快照”,这样就能保证能够获取到所有的数据,但是无法保证数据的实时性。 总结ConcurrentHashMap 是一个线程安全、高效的 Map 实现,它采用了分段锁的思想,可以有效的降低锁的粒度,提高并发度。它的扩容机制与 HashMap 的扩容机制有很大的不同,能够在不影响性能的情况下进行扩容。同时,它提供了两种弱一致性的迭代器,能够保证能够获取到所有的数据,但是无法保证数据的实时性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值