Hashtable 和 HashMap 以及 ConcurrentHashMap
备忘: ConcurrentHashMap与Hashtable的区别: Hashtable中采用的锁机制是一次锁住整个hash表,从而同一时刻只能由一个线程对其进行操作;而ConcurrentHashMap中则是一次锁住一个桶, 同时可以有多个线程对其进行操作。
Map用于存储“key-value”元素对,它将一个key映射到一个而且只能是唯一的一个value。
Map可以使用多种实现方式,HashMap的实现采用的是hash表;而TreeMap采用的是红黑树。
1. Hashtable 和 HashMap
这两个类主要有以下几方面的不同:
Hashtable和HashMap都实现了Map接口,但是Hashtable的实现是基于Dictionary抽象类。
在HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。 当get()方法返回null值时,即可以表示 HashMap中没有该键,也可以表示该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键,而应该用containsKey()方法来判断。而在Hashtable中,无论是key还是value都不能为null 。
这两个类最大的不同在于Hashtable是线程安全的,它的方法是同步了的,可以直接用在多线程环境中。而HashMap则不是线程安全的。在多线程环境中,需要手动实现同步机制。因此,在Collections类中提供了一个方法返回一个同步版本的HashMap用于多线程的环境:
- public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {
- return new SynchronizedMap<K,V>(m);
- }
该方法返回的是一个SynchronizedMap 的实例。SynchronizedMap类是定义在Collections中的一个静态内部类。它实现了Map接口,并对其中的每一个方法实现,通过synchronized 关键字进行了同步控制。
2. 潜在的线程安全问题
上面提到Collections为HashMap提供了一个并发版本SynchronizedMap。这个版本中的方法都进行了同步,但是这并不等于这个类就一定是线程安全的。在某些时候会出现一些意想不到的结果。
如下面这段代码:
- // shm是SynchronizedMap的一个实例
- if(shm.containsKey('key')){
- shm.remove(key);
- }
这段代码用于从map中删除一个元素之前判断是否存在这个元素。这里的containsKey和reomve方法都是同步的,但是整段代码却不是。考虑这么一个使用场景:线程A执行了containsKey方法返回true,准备执行remove操作;这时另一个线程B开始执行,同样执行了containsKey方法返回true,并接着执行了remove操作;然后线程A接着执行remove操作时发现此时已经没有这个元素了。要保证这段代码按我们的意愿工作,一个办法就是对这段代码进行同步控制,但是这么做付出的代价太大。
在进行迭代时这个问题更改明显。Map集合共提供了三种方式来分别返回键、值、键值对的集合:
- Set<K> keySet();
- Collection<V> values();
- Set<Map.Entry<K,V>> entrySet();
在这三个方法的基础上,我们一般通过如下方式访问Map的元素:
- Iterator keys = map.keySet().iterator();
- while(keys.hasNext()){
- map.get(keys.next());
- }
在这里,有一个地方需要注意的是:得到的keySet和迭代器都是Map中元素的一个“视图”,而不是“副本” 。问题也就出现在这里,当一个线程正在迭代Map中的元素时,另一个线程可能正在修改其中的元素。此时,在迭代元素时就可能会抛出 ConcurrentModificationException异常。为了解决这个问题通常有两种方法,一是直接返回元素的副本,而不是视图。这个可以通过
集合类的 toArray() 方法实现,但是创建副本的方式效率比之前有所降低,特别是在元素很多的情况下;另一种方法就是在迭代的时候锁住整个集合,这样的话效率就更低了。
3. 更好的选择:ConcurrentHashMap
java5中新增了ConcurrentMap接口和它的一个实现类ConcurrentHashMap。ConcurrentHashMap提供了和Hashtable以及SynchronizedMap中所不同的锁机制。Hashtable中采用的锁机制是一次锁住整个hash表,从而同一时刻只能由一个线程对其进行操作;而ConcurrentHashMap中则是一次锁住一个桶。ConcurrentHashMap默认将hash表分为16个桶,诸如get,put,remove等常用操作只锁当前需要用到的桶。这样,原来只能一个线程进入,现在却能同时有16个写线程执行,并发性能的提升是显而易见的。
上面说到的16个线程指的是写线程,而读操作大部分时候都不需要用到锁。只有在size等操作时才需要锁住整个hash表。
在迭代方面,ConcurrentHashMap使用了一种不同的迭代方式。在这种迭代方式中,当iterator被创建后集合再发生改变就不再是抛出ConcurrentModificationException,取而代之的是在改变时new新的数据从而不影响原有的数据 ,iterator完成后再将头指针替换为新的数据 ,这样iterator线程可以使用原来老的数据,而写线程也可以并发的完成改变。
Hashtable 和 HashMap 以及 ConcurrentHashMap的更多相关文章
- HashTable、HashMap与ConCurrentHashMap源码解读
HashMap 的数据结构 hashMap 初始的数据结构如下图所示,内部维护一个数组,然后数组上维护一个单链表,有个形象的比喻就是想挂钩一样,数组脚标一样的,一个一个的节点往下挂. 我们可以 ...
- HashTable、HashMap、ConcurrentHashMap的区别
HashTable是做了同步的,HashMap未考虑同步.所以HashMap在单线程情况下效率较高:HashTable在的多线程情况下,同步操作能保证程序执行的正确性. HashMap是非线程安全的, ...
- HashTable、HashMap、ConcurrentHashMap、Collections.synchronizedMap()区别
Collections.synchronizedMap()和Hashtable一样,实现上在调用map所有方法时,都对整个map进行同步,而ConcurrentHashMap的实现却更加精细,它对Ha ...
- 【转】HashMap、TreeMap、Hashtable、HashSet和ConcurrentHashMap区别
转自:http://blog.csdn.net/paincupid/article/details/47746341 一.HashMap和TreeMap区别 1.HashMap是基于散列表实现的,时间 ...
- ConcurrentHashMap、HashTable、HashMap的区别
HashTable与ConcurrentHashMap: 相同点:都是线程安全的,可以在多线程的环境下运行.key和value都不能为null 区别:性能上的差异.HashTable每次操作对象都会锁 ...
- 多线程之Map:Hashtable HashMap 以及ConcurrentHashMap
1.Map体系参考:http://java.chinaitlab.com/line/914247.htmlHashtable是JDK 5之前Map唯一线程安全的内置实现(Collections.syn ...
- 沉淀再出发:java中的HashMap、ConcurrentHashMap和Hashtable的认识
沉淀再出发:java中的HashMap.ConcurrentHashMap和Hashtable的认识 一.前言 很多知识在学习或者使用了之后总是会忘记的,但是如果把这些只是背后的原理理解了,并且记忆下 ...
- HashMap与ConcurrentHashMap、HashTable
(1)HashMap的线程不安全原因一:死循环 原因在于HashMap在多线程情况下,执行resize()进行扩容时容易造成死循环. 扩容思路为它要创建一个大小为原来两倍的数组,保证新的容量仍为2的N ...
- HashMap和ConcurrentHashMap和HashTable的底层原理与剖析
HashMap 可以允许key为null,value为null,但HashMap的是线程不安全的 HashMap 底层是数组 + 链表的数据结构 在jdk 1.7 中 map集合中的每一项都是一个 ...
随机推荐
- Qt-网易云音乐界面实现-3 音乐名片模块的实现
这个模块其实我是不知道该叫什么的,暂时就叫做音乐名片模块吧,这可以看到,这个模块简单的显示以下信息. 1. 歌曲名称 2. 歌曲歌唱者 3. 歌曲封面 4. 喜欢歌曲的按钮 5. 分享歌曲的按钮 6. ...
- 通过Task异步加快对数组的运算
一.介绍 Task是.NetFramework3.0出现的,线程是基于线程池,然后提供了丰富的API. 先用AverageAssign方法把一组数据平均分成n组,再通过遍历n组数据,循环开Task多线 ...
- Mac OS下Appium环境搭建及Genymotion模拟器安装
说明:本机Mac本上已经安装的有:Python3.JDK.Node.js. Selenium Appium环境搭建 Step1:安装Appium Python Client库 1,命令:pip ins ...
- log4cpp简单使用及踩到的坑
log4cpp是log4j的一个扩展, C++开发者可用该库记录日志,可输出到终端,亦可保存到文件. 下面简单demo展示如何输出日志到输出终端. #include <iostream> ...
- 6. 使用antd pro构建web页面
前言 在开始之前,希望我们已经掌握了一部分react的知识,由于没有太多经验,其实我也是属于摸索阶段.这里假定我们已经了解了react,redux和dva/umi相关的知识.并有做过相关练习. 如果还 ...
- SparkRDD编程实战
通过spark实现点击流日志分析案例 1. 访问的pv package cn.itcast import org.apache.spark.rdd.RDD import org.apache.spar ...
- Android NDK 工具链的使用方法(Standalone Toolchain)
转载:http://blog.csdn.net/smfwuxiao/article/details/6587709 首先需要确定目标机器的指令集. 如果是 x86 的机器,用 x86-4.4.3 版本 ...
- 第33次Scrum会议(11/21)【欢迎来怼】
一.小组信息 队名:欢迎来怼小组成员队长:田继平成员:李圆圆,葛美义,王伟东,姜珊,邵朔,阚博文 小组照片 二.开会信息 时间:2017/11/21 11:35~11:57,总计22min.地点:东北 ...
- java_web连接SQL_server详细步骤
(1).我用的是Myeclipse,可以直接将sqljdbc4.jar拷到项目文件 (2).点开SQL Server配置管理器 选中SQL Server2008网络配置下的SQLEXPRESS的协议, ...
- 对Largest函数的测试
题目:查找list[]中的最大值:int Largest(int list[], int length); int Largest(int list[], int length) { int i,ma ...