Java 中ConcurrentHashMap的实现_java

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的实现和使用场景,希望能帮助到大家!谢谢大家对本站的支持!

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索java
concurrenthashmap
concurrenthashmap、concurrenthashmap 锁、concurrenthashmap 读、java currenthashmap、java hashmap实现原理,以便于您获取更多的相关知识。

时间: 2024-09-12 06:38:09

Java 中ConcurrentHashMap的实现_java的相关文章

详解java中的Collections类_java

一般来说课本上的数据结构包括数组.单链表.堆栈.树.图.我这里所指的数据结构,是一个怎么表示一个对象的问题,有时候,单单一个变量声明不堪大用,比如int,String,double甚至一维数组.二维数组无法完全表达你要表达的东西,而定义一个类Class有太过麻烦,这时候,你可以考虑一下用Java中的Collections类.使用Collections类,必须在文件头声明import java.util.*; 一.动态.有序.可变大小的一维数组Vector与ArrayList  Collectio

详解Java中final的用法_java

概念 final 具有"不可改变的"的含义,可以修饰 非抽象类.非抽象成员方法和变量. 用 final 修饰的类不能被继承,没有子类. 用 final 修饰的方法不能被子类的方法覆盖(重写). 用 final 修饰的变量表示常量,只能被赋一次值(声明变量的时候). 注: final 不能用来修饰构造方法,因为"方法覆盖"这一概念仅适用于类的成员方法,而不适用于类的构造方法,父类的构造方法和子类的构造方法之间不存在覆盖的关系,因此用final修饰构造方法没有任何意义.

深入理解java中的synchronized关键字_java

synchronized 关键字,代表这个方法加锁,相当于不管哪一个线程A每次运行到这个方法时,都要检查有没有其它正在用这个方法的线程B(或者C D等),有的话要等正在使用这个方法的线程B(或者C D)运行完这个方法后再运行此线程A,没有的话,直接运行它包括两种用法:synchronized 方法和 synchronized 块. 1. synchronized 方法:通过在方法声明中加入 synchronized关键字来声明 synchronized 方法.如: 复制代码 代码如下: publ

快速入门Java中的Lambda表达式_java

Lambda简介 Lambda表达式是Java SE 8中一个重要的新特性.lambda表达式允许你通过表达式来代替功能接口. lambda表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体(body,可以是一个表达式或一个代码块). Lambda表达式还增强了集合库. Java SE 8添加了2个对集合数据进行批量操作的包: java.util.function 包以及 java.util.stream 包. 流(stream)就如同迭代器(iterator),但附加了许多

深入理解Java中的Lambda表达式_java

 Java 8 开始出现,带来一个全新特性:使用 Lambda 表达式 (JSR-335) 进行函数式编程.今天我们要讨论的是 Lambda 的其中一部分:虚拟扩展方法,也叫做公共辩护(defender)方法.该特性可以让你在接口定义中提供方法的默认实现.例如你可以为已有的接口(如 List 和 Map)声明一个方法定义,这样其他开发者就无需重新实现这些方法,有点像抽象类,但实际却是接口.当然,Java 8 理论上还是兼容已有的库. 虚拟扩展方法为 Java 带来了多重继承的特性,尽管该团队声称

面向对象编程:Java中的抽象数据类型_Java编程

文章来源:互联网 作者:PaleSting/CSDN 在本文中,我们将考察Java中的数据类型,但是我们将介绍抽象数据类型(ADT)的概念.我们还将通过介绍Java Collections Framework(Java 集合架构)来学习Java定义的一些ADT. ADT 一个ADT是一个仅由保存的数据类型和可能在这个数据类型上进行的操作定义的.开发者们只能通过ADT的操作方法来访问ADT的属性,而且他们不会知道这个数据类型内部各种操作是如何实现的. 在Java中,我们常常使用一个接口来给出一个操

深入剖析java中的集合框架_java

解析:如果并不知道程序运行时会需要多少对象,或者需要更复杂方式存储对象,那么可以使用Java集合框架. 如果启用集合的删除方法,那么集合中所有元素的索引会自动维护. 集合完全弥补了数组的缺陷. 02.集合框架的内容  集合框架都包含三大块内容:对外的接口,接口的实现和对集合运算的算法  01.接口:表示集合的抽象数据类型  02.实现:集合框架中接口的具体实现  03.算法:在一个实现了某个集合框架的接口的对象身上完成某种有用的计算方法 java集合框架简图:    01.Collection接

解决Java中OutOfMemoryError的问题_java

目前为止,我遇到使用Tomcat有三种情况:第一,使用Eclipse,在Eclipse中配置Tomcat.第二,直接在Tomcat中部署项目.第三将Tomcat安装为windows服务. 在这三种情况下,出现OutOfMemoryError.该怎么解决呢?这里我不得不提我被网上那些不负责任的文章害得很惨.各种设置内存的方法都试了,可就是不起作用.下面我说的这几种方法都是我亲自试验过的,没有问题. 第一种情况:  如图:我用红色框框出来的.其中Xms和Xmx是增加java虚拟机初始堆大小和最大堆大

Java中的匿名内部类小结_java

java内部类分为: 成员内部类.静态嵌套类.方法内部类.匿名内部类 . 在java的世界里,提供了匿名内部类语法,用于帮助大家简化代码,本文简要从接口,抽象类以及常规类以代码的形式描述其常用模式. 1. 接口模式 public interface IWriter { void write(); } public static void main(String[] args) { IWriter writer = new IWriter() { @Override public void wri