ConcurrentHashMap

ConcurrentHashMap在Java1.5中作为线程安全的Map引入,性能优于HashTable。通过分割Map并使用分段锁,允许多个写线程同时操作不同部分,提高并发性能。本文解析其工作原理,包括弱一致性迭代器、null键值处理及替代put操作的putIfAbsent方法。

ConcurrentHashMap(简称CHM)是在Java 1.5作为Hashtable的替代选择新引入的,是concurrent包的重要成员

在Java 1.5之前,如果想要实现一个可以在多线程和并发的程序中安全使用的Map,只能在HashTable和synchronized Map中选择

  • 因为HashMap并不是线程安全的
  • ConcurrentHashMap不但是线程安全的,而且比HashTable和synchronizedMap的性能要好
  • 根据默认的并发级别(concurrency level),Map被分割成16个部分,并且由不同的锁控制
    • 这意味着,同时最多可以有16个写线程操作Map
    • 试想一下,由只能一个线程进入变成同时可由16个写线程同时进入(读线程几乎不受限制),性能的提升是显而易见的
    • 在迭代遍历CHM时,keySet返回的iterator是弱一致和fail-safe的,可能不会返回某些最近的改变
    • 并且在遍历过程中,如果已经遍历的数组上的内容变化了,不会抛出ConcurrentModificationExceptoin的异常

很多时候我们希望在元素不存在时插入元素,我们一般会像下面那样写代码

ae8b4a290272c0565900d3143d63f4d5f95.jpg

  • 上面这段代码在HashMap和HashTable中是好用的,但在CHM中是有出错的风险的
    • CHM的比HashTable的同步性稍弱
    • 这是因为CHM在put操作时并没有对整个Map加锁,所以一个线程正在put(k,v)的时候
    • 另一个线程调用get(k)会得到null,这就会造成一个线程put的值会被另一个线程put的值所覆盖
    • 这就是对segment 加锁的原因
  • 上面的map 仅仅是锁对象,不代表别的线程不能使用map ,仅仅代表别的线程不能进入该代码块
  • CHM提供的putIfAbsent(key,value)方法原子性的实现了同样的功能,同时避免了上面的线程竞争的风险

总结

  • CHM允许并发的读和线程安全的更新操作
  • 在执行写操作时,CHM只锁住部分的Map
  • 并发的更新是通过内部根据并发级别将Map分割成小部分实现的
  • 高的并发级别会造成时间和空间的浪费,低的并发级别在写线程多时会引起线程间的竞争
  • CHM的所有操作都是线程安全
  • CHM返回的迭代器是弱一致性,fail-safe并且不会抛出ConcurrentModificationException异常
    • 因为每次更新内容时,仅仅是锁一个segment,不是整个map锁定
  • CHM不允许null的键值
  • 可以使用CHM代替HashTable,但要记住CHM不会锁住整个Map

从ConcurrentHashMap代码中可以看出,它引入了一个“分段锁”的概念

  • 具体可以理解为把一个大的Map拆分成N个小的HashTable,根据key.hashCode()来决定把key放到哪个HashTable中
  • 就是把Map分成了N个Segment,put和get的时候,都是现根据key.hashCode()算出放到哪个Segment中

转载于:https://my.oschina.net/u/3847203/blog/2251140

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值