HashMap、HashTable与ConcurrentHashMap的区别
1、HashTable与HashMap
(1)HashTable和HashMap都实现了Map接口,但是HashTable的实现是基于Dictionary抽象类。
(2)在HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。当get()方法返回null值时,既可以表示HashMap中没有该键,也可以表示该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键,而应该用containsKey()方法来判断。而在HashTable中,无论是key还是value都不能为null。
(3)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()和remove()方法都是同步的,但是整段代码却不是。
考虑这么一个场景:线程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异常。
为了解决这个问题,通常有两种方法:
(1)一是直接返回元素的副本,而不是视图。这个可以通过集合类的toArray()方法实现,但是创建副本的方式效率比之前有所降低,特别是在元素很多的情况下;
(2)另一种方法就是在迭代的时候锁住整个集合,这样的话效率就更低了。
3、更好的选择:ConcurrentHashMap
Java 5中新增了ConcurrentMap接口和它的实现类ConcurrentHashMap。
ConcurrentHashMap提供了和HashTable以及SynchronizedMap中所不同的锁机制。HashTable中采用的锁机制是一次锁住整个hash表,从而同一时刻只能有一个线程对其进行操作;而ConcurrentHashMap中则是一次锁住一个桶。ConcurrentHashMap默认将hash表分为16个桶,诸如get,put,remove等常用操作只锁住当前需要用到的桶。这样,原来只能一个线程进入,现在却能同时有16个写线程执行,并发性能的提升是显而易见的。
上面说到的16个线程指的是写线程,而读操作大部分时候都不需要用到锁。只有在size等操作时才需要锁住整个hash表。
在迭代方面,ConcurrentHashMap使用了一种不同的迭代方式。在这种迭代方式中,当iterator被创建后集合再发生改变就不再是抛出ConcurrentModificationException,取而代之的是,在改变时new新的数据从而不影响原有的数据,iterator完成后再将头指针替换为新的数据,这样iterator可以使用原来老的数据,而写线程也可以并发的完成改变。
HashMap、HashTable与ConcurrentHashMap的区别的更多相关文章
- HashMap HashTable和ConcurrentHashMap的区别
HashMap和Hashtable都实现了Map接口,其主要的区别有:线程安全性,同步(synchronization),以及效率. HashMap和Hashtable基本上没啥区别,除了HashMa ...
- 集合 HashMap 的原理,与 Hashtable、ConcurrentHashMap 的区别
一.HashMap 的原理 1.HashMap简介 简单来讲,HashMap底层是由数组+链表的形式实现,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的,如果定位到的数组位置不含链表 ...
- Java Main Differences between HashMap HashTable and ConcurrentHashMap
转自这篇帖子:http://www.importnew.com/7010.html HashMap和Hashtable的比较是Java面试中的常见问题,用来考验程序员是否能够正确使用集合类以及是否可以 ...
- HashMap和Hashtable以及ConcurrentHashMap的区别
HashMap和Hashtable的区别 何为HashMap HashMap是在JDK1.2中引入的Map的实现类. HashMap是基于哈希表实现的,每一个元素是一个key-value对,其内部 ...
- HashMap、HashTable、ConcurrentHashMap的区别
一.相关概念 1.Map的概念 javadoc中对Map的解释如下: An objectthat maps keys to values . Amap cannot contain duplicate ...
- HashMap、Hashtable和ConcurrentHashMap的区别
HashTable 底层数组+链表实现,无论key还是value都不能为null,线程安全,实现线程安全的方式是在修改数据时锁住整个HashTable,效率低,ConcurrentHashMap做了相 ...
- HashMap,HashTable ,LinkedHashMap,TreeMap的区别
Map:主要是存储键值对,不允许键重复,但可以值重复. HashMap:根据键的HashCode值来存储数据,根据键直接获取值.具有很快的访问速度,遍历时,取得的数据值的顺序都是随机的.hashMap ...
- HashTable与ConcurrentHashMap的区别
- HashMap、HashTable、ConcurrentHashMap、HashSet区别 线程安全类
HashMap专题:HashMap的实现原理--链表散列 HashTable专题:Hashtable数据存储结构-遍历规则,Hash类型的复杂度为啥都是O(1)-源码分析 Hash,Tree数据结构时 ...
随机推荐
- Brief Introduction to SDK – JRE – JVM – JIT
Brief Introduction to SDK – JRE – JVM – JIT SDK This is complete collection of Java stuff, as it has ...
- Object archiving
https://developer.apple.com/library/content/documentation/General/Conceptual/DevPedia-CocoaCore/Arch ...
- NYOJ469 - 擅长排列的小明 II - (dp)
题目描述: 小明十分聪明,而且十分擅长排列计算. 有一天小明心血来潮想考考你,他给了你一个正整数n,序列1,2,3,4,5......n满足以下情况的排列: 1.第一个数必须是1 2.相邻两个数之差不 ...
- java垃圾回收及gc全面解析(全面覆盖cms、g1、zgc、openj9)
一般来说,gc的停顿时间和活跃对象的堆大小成比例,视gc线程的数量,每1GB可能会停顿1-3秒,且cpu数量通常和gc呈现阿姆达尔定律(Amdahl’s Law),而非我们直观计算的线性变化.如下: ...
- artillery强大灵活的负载测试套件
artillery是基于nodejs 编写的负载测试套件支持http&&socket.io&&websockets&&kinesis&& ...
- 彻底理解C++指针
目录 目录 1 1. 概念 1 1.1. 双指针 1 1.2. 指针数组 1 1.3. 数组指针 1 1.4. 常见指针定义解读 1 2. 区别 2 3. 兼容性 2 4. 为何列数须相等? 2 5. ...
- send 和recv小结
不论是客户还是服务器应用程序都用send函数来向TCP连接的另一端发送数据. 不论是客户还是服务器应用程序都用recv函数从TCP连接的另一端接收数据. #include <sys/socket ...
- CCF 201803-3 URL映射
CCF 201803-3 URL映射 试题编号: 201803-3 试题名称: URL映射 时间限制: 1.0s 内存限制: 256.0MB 问题描述: 问题描述 URL 映射是诸如 Django. ...
- 大数定律(Law of Large Numbers)
大数定律:每次从总体中随机抽取1个样本,这样抽取很多次后,样本的均值会趋近于总体的期望.也可以理解为:从总体中抽取容量为n的样本,样本容量n越大,样本的均值越趋近于总体的期望.当样本容量极大时,样本均 ...
- 记一次phpmyadmin 4.8.1 远程文件包含漏洞(BUUCTF web)
题目很简单,一个滑稽 打开源码,发现存在source.php文件 于是访问文件,发现出现一串php源码 提示存在hint.php,于是访问发现一句话 flag not here, and flag i ...