Java 中ConcurrentHashMap的实现

ConcurrentHashMap(简称CHM)是在Java 1.5作为Hashtable的替代选择新引入的,是concurrent包的重要成员。在Java 1.5之前,如果想要实现一个可以在多线程和并发的程序中安全使用的Map,只能在HashTable和synchronized Map中选择,因为HashMap并不是线程安全的。但再引入了CHM之后,我们有了更好的选择。CHM不但是线程安全的,而且比HashTable和synchronizedMap的性能要好。相对于HashTable和synchronizedMap锁住了整个Map,CHM只锁住部分Map。CHM允许并发的读操作,同时通过同步锁在写操作时保持数据完整性。我们已经在Top 5 Java Concurrent Collections from JDK 5 and 6中学习了CHM的基础知识,在这篇博客中我将介绍以下几点:

  1. CHM在Java中如何实现的
  2. 什么情况下应该使用CHM
  3. 在Java中使用CHM的例子
  4. CHM的一些重要特性

Java中ConcurrentHashMap的实现

CHM引入了分割,并提供了HashTable支持的所有的功能。在CHM中,支持多线程对Map做读操作,并且不需要任何的blocking。这得益于CHM将Map分割成了不同的部分,在执行更新操作时只锁住一部分。根据默认的并发级别(concurrency level),Map被分割成16个部分,并且由不同的锁控制。这意味着,同时最多可以有16个写线程操作Map。试想一下,由只能一个线程进入变成同时可由16个写线程同时进入(读线程几乎不受限制),性能的提升是显而易见的。但由于一些更新操作,如put(),remove(),putAll(),clear()只锁住操作的部分,所以在检索操作不能保证返回的是最新的结果。

另一个重要点是在迭代遍历CHM时,keySet返回的iterator是弱一致和fail-safe的,可能不会返回某些最近的改变,并且在遍历过程中,如果已经遍历的数组上的内容变化了,不会抛出ConcurrentModificationExceptoin的异常。

CHM默认的并发级别是16,但可以在创建CHM时通过构造函数改变。毫无疑问,并发级别代表着并发执行更新操作的数目,所以如果只有很少的线程会更新Map,那么建议设置一个低的并发级别。另外,CHM还使用了ReentrantLock来对segments加锁。

Java中ConcurrentHashMap putifAbsent方法的例子

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

synchronized(map){
 if (map.get(key) == null){
  return map.put(key, value);
 } else{
  return map.get(key);
 }
}

上面这段代码在HashMap和HashTable中是好用的,但在CHM中是有出错的风险的。这是因为CHM在put操作时并没有对整个Map加锁,所以一个线程正在put(k,v)的时候,另一个线程调用get(k)会得到null,这就会造成一个线程put的值会被另一个线程put的值所覆盖。当然,你可以将代码封装到synchronized代码块中,这样虽然线程安全了,但会使你的代码变成了单线程。CHM提供的putIfAbsent(key,value)方法原子性的实现了同样的功能,同时避免了上面的线程竞争的风险。

什么时候使用ConcurrentHashMap

CHM适用于读者数量超过写者时,当写者数量大于等于读者时,CHM的性能是低于Hashtable和synchronized Map的。这是因为当锁住了整个Map时,读操作要等待对同一部分执行写操作的线程结束。CHM适用于做cache,在程序启动时初始化,之后可以被多个请求线程访问。正如Javadoc说明的那样,CHM是HashTable一个很好的替代,但要记住,CHM的比HashTable的同步性稍弱。

总结

现在我们知道了什么是ConcurrentHashMap和什么时候该用ConcurrentHashMap,下面我们来复习一下CHM的一些关键点。

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

以上就是Java中CHM的实现和使用场景,希望能帮助到大家!谢谢大家对本站的支持!

(0)

相关推荐

  • java 使用ConcurrentHashMap和计数器实现锁

    java 使用ConcurrentHashMap和计数器实现锁 在某些场景下,我们想让线程根据某些业务数据进行排队,简单代码如下: import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.Ato

  • Java 中ConcurrentHashMap的实现

    ConcurrentHashMap(简称CHM)是在Java 1.5作为Hashtable的替代选择新引入的,是concurrent包的重要成员.在Java 1.5之前,如果想要实现一个可以在多线程和并发的程序中安全使用的Map,只能在HashTable和synchronized Map中选择,因为HashMap并不是线程安全的.但再引入了CHM之后,我们有了更好的选择.CHM不但是线程安全的,而且比HashTable和synchronizedMap的性能要好.相对于HashTable和sync

  • Java中ConcurrentHashMap是如何实现线程安全

    目录 语法: ConcurrentHashmap 的需要: 如何使 ConcurrentHashMap 线程安全成为可能? Hashtable.Hashmap.ConcurrentHashmap的区别 ConcurrentHashMap是一个哈希表,支持检索的全并发和更新的高预期并发.此类遵循与 Hashtable 相同的功能规范,并包含 Hashtable 的所有方法.ConcurrentHashMap 位于 java.util.Concurrent 包中. 语法: public class

  • java中ConcurrentHashMap的读操作为什么不需要加锁

    前言 ConcurrentHashMap是Java 5中支持高并发.高吞吐量的线程安全HashMap实现. 我们知道,ConcurrentHashmap(1.8)这个并发集合框架是线程安全的,当你看到源码的get操作时,会发现get操作全程是没有加任何锁的,这也是这篇博文讨论的问题--为什么它不需要加锁呢? 下面话不多说了,来一起看看详细的介绍吧 ConcurrentHashMap的简介 我想有基础的同学知道在jdk1.7中是采用Segment + HashEntry + ReentrantLo

  • Java中遍历ConcurrentHashMap的四种方式详解

    这篇文章主要介绍了Java中遍历ConcurrentHashMap的四种方式详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 方式一:在for-each循环中使用entries来遍历 System.out.println("方式一:在for-each循环中使用entries来遍历");for (Map.Entry<String, String> entry: map.entrySet()) { System.out.pr

  • Java的ConcurrentHashMap中不能存储null的原因解析

    目录 一.先出源码出发 二.那么究竟这是为什么呢? 三.ConcurrentHashMap 作者 Doug Lea 的邮件 众所周知,在Java中Map可以存储null,而ConcurrentHashMap不能存储null值,那么为什么呢? 一.先出源码出发 put方法点进去~ @throws NullPointerException if the specified key or value is null and this map does not permit null keys or v

  • obix协议在java中的配置和使用详解

    前言 本文主要给大家介绍的是关于obix协议在java中的配置和使用,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 什么是 oBIX? 简单来讲,obix是一种 XML 通讯协议,使用Http Request/Post方式进行数据通讯.所有数据通过可读字符进行传送,一个oBIX对象可以有唯一的一个URL识别. oBIX的实现原理 首先数据储存在Niagara的服务平台上,我们需要做的是从Niagara获取数据,并且储存在InfluxDB中.下面是实现的流程方法. 加粗 Ctr

  • Java中Map的排序问题详解

    Map的种类 在Java中,Map的主要作用是存储键值对.由于是根据键得到值,所以不允许键重复.它主要有如下几个类别: HashMap: 最常用的Map,它根据键的HashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度,遍历时,取得数据的顺序是完全随机的.HashMap最多只允许一条记录的键为Null;允许多条记录的值为Null;HashMap不支持线程的同步,即任一时刻可以有多个线程同时写HashMap;可能会导致数据的不一致.如果需要同步,可以用Collections的sy

  • Java中同步与并发用法分析

    本文较为详细的分析了Java中同步与并发的用法.分享给大家供大家参考.具体分析如下: 1.同步容器类包括两部分:vector和hashtable 另一类是同步包装类,由Collections.synchronizedXXX创建.同步容器对容器的所有状态进行串行访问,从而实现线程安全. 它们存在如下问题: a) 对于符合操作,需要额外的锁保护.比如迭代,缺少则添加等条件运算. b) toString,hashCode,equals都会间接的调用迭代,都需要注意并发.   2.java5.0中的并发

  • java 中锁的性能提高办法

    java 中锁的性能提高办法 我们努力为自己的产品所遇到的问题思考解决办法,但在这篇文章中我将给大家分享几种常用的技术,包括分离锁.并行数据结构.保护数据而非代码.缩小锁的作用范围,这几种技术可以使我们不使用任何工具来检测死锁. 锁不是问题的根源,锁之间的竞争才是 通常在多线程的代码中遇到性能方面的问题时,一般都会抱怨是锁的问题.毕竟锁会降低程序的运行速度和其较低的扩展性是众所周知的.因此,如果带着这种"常识"开始优化代码,其结果很有可能是在之后会出现讨人厌的并发问题. 因此,明白竞争

  • Java中的锁分类的详细介绍

    在读很多并发文章中,会提及各种各样锁如公平锁,乐观锁等等,这篇文章介绍各种锁的分类.介绍的内容如下: 公平锁/非公平锁 可重入锁 独享锁/共享锁 互斥锁/读写锁 乐观锁/悲观锁 分段锁 偏向锁/轻量级锁/重量级锁 自旋锁 上面是很多锁的名词,这些分类并不是全是指锁的状态,有的指锁的特性,有的指锁的设计,下面总结的内容是对每个锁的名词进行一定的解释. 公平锁/非公平锁 公平锁是指多个线程按照申请锁的顺序来获取锁. 非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的

随机推荐