我有一个Map,它将被多个线程同时修改.
Java API中似乎有三种不同的同步Map实现:
Hashtable
Collections.synchronizedMap(Map)
ConcurrentHashMap
据我所知,这Hashtable
是一个旧的实现(扩展过时的Dictionary
类),后来经过调整以适应Map
界面.虽然它是同步的,但似乎存在严重的可扩展性问题,并且不鼓励新项目.
但是其他两个怎么样?返回的地图Collections.synchronizedMap(Map)
和ConcurrentHashMap
s 之间有什么区别?哪一种适合哪种情况?
根据您的需求,使用ConcurrentHashMap
.它允许从多个线程并发地修改Map,而无需阻止它们.Collections.synchronizedMap(map)
创建阻塞Map会降低性能,尽管确保一致性(如果使用得当).
如果需要确保数据一致性,请使用第二个选项,并且每个线程都需要具有最新的地图视图.如果性能至关重要,请使用第一个,并且每个线程仅将数据插入到地图中,读取发生的频率较低.
???????????????????????????????????????????????????????????????????????????????
? Property ? HashMap ? Hashtable ? ConcurrentHashMap ?
???????????????????????????????????????????????????????????????????????????????
? Null ? allowed ? not allowed ?
? values/keys ? ? ?
???????????????????????????????????????????????????????????????????????????????
?Is thread-safe ? no ? yes ?
???????????????????????????????????????????????????????????????????????????????
? Lock ? not ? locks the whole ? locks the portion ?
? mechanism ? applicable ? map ? ?
???????????????????????????????????????????????????????????????????????????????
? Iterator ? fail-fast ? weakly consistent ?
???????????????????????????????????????????????????????????????????????????????
关于锁定机制:
Hashtable
锁定对象,同时仅ConcurrentHashMap
锁定桶.
"可扩展性问题"以Hashtable
完全相同的方式呈现Collections.synchronizedMap(Map)
- 它们使用非常简单的同步,这意味着只有一个线程可以同时访问地图.
当你有简单的插入和查找时(除非你非常密集地进行),这不是什么大问题,但是当你需要遍历整个Map时会出现一个大问题,这可能需要很长时间才能完成一个大的Map - 而一个线程就是这样做的,所有其他人都必须等待,如果他们想插入或查找任何东西.
在ConcurrentHashMap
使用非常复杂的技术,以减少同步的需要,并允许由多个线程并行读取访问,而无需同步,更重要的是,提供了Iterator
一个无需同步,甚至允许地图互为作用过程中被修改(尽管它不保证是否不会返回在迭代期间插入的元素).
当你可以使用它时,首选ConcurrentHashMap - 尽管它至少需要Java 5.
它被设计为在被多个线程使用时可以很好地扩展.当一次只有一个线程访问Map时,性能可能稍差,但当多个线程同时访问映射时,性能会明显提高.
我发现了一个博客条目,它从优秀的书籍Java Concurrency In Practice中重现了一个表,我完全推荐了这本书.
Collections.synchronizedMap真正有意义,只有当你需要包含一些具有其他特征的地图时,可能是某种有序的地图,比如TreeMap.
这两者之间的主要区别在于,它ConcurrentHashMap
只会锁定正在更新的部分数据,而其他部分数据可以被其他线程访问.但是,Collections.synchronizedMap()
在更新时会锁定所有数据,其他线程只能在释放锁时访问数据.如果有许多更新操作和相对少量的读取操作,则应选择ConcurrentHashMap
.
另外一个区别是ConcurrentHashMap
不会保留传入Map的元素顺序.它与HashMap
存储数据时类似.无法保证元素顺序得以保留.虽然Collections.synchronizedMap()
将保留传入的Map的元素顺序.例如,如果传递TreeMap
给ConcurrentHashMap
,则元素顺序ConcurrentHashMap
可能与顺序中的顺序不同TreeMap
,但Collections.synchronizedMap()
会保留顺序.
此外,ConcurrentHashMap
可以保证ConcurrentModificationException
在一个线程更新映射并且另一个线程正在遍历从映射获得的迭代器时没有抛出.但是,Collections.synchronizedMap()
不保证这一点.
有一个帖子证明了这两者之间的差异ConcurrentSkipListMap
.
Hashtable
并且ConcurrentHashMap
不允许null
键或null
值.
Collections.synchronizedMap(Map)
同步所有操作(get
,put
,size
等).
ConcurrentHashMap
支持检索的完全并发,以及可更新的预期并发性.
像往常一样,涉及并发 - 开销 - 速度权衡.您确实需要考虑应用程序的详细并发要求来做出决策,然后测试代码以确定它是否足够好.
在ConcurrentHashMap
,锁定应用于段而不是整个Map.每个段管理自己的内部哈希表.锁仅适用于更新操作.Collections.synchronizedMap(Map)
同步整个地图.
同步地图:
同步映射与Hashtable也没有太大差别,并且在并发Java程序中提供类似的性能.Hashtable和SynchronizedMap之间的唯一区别是SynchronizedMap不是遗留的,您可以使用Collections.synchronizedMap()方法包装任何Map以创建它的同步版本.
ConcurrentHashMap的:
ConcurrentHashMap类提供标准HashMap的并发版本.这是对Collections类中提供的synchronizedMap功能的改进.
与Hashtable和Synchronized Map不同,它永远不会锁定整个Map,而是将段分割成段,并在那些上完成锁定.如果读取器线程的数量大于写入器线程的数量,则它的性能会更好.
默认情况下,ConcurrentHashMap分为16个区域并应用锁定.初始化ConcurrentHashMap实例时,可以设置此默认编号.在特定段中设置数据时,将获取该段的锁定.这意味着如果两个更新各自影响单独的存储桶,则它们仍可以安全地同时执行,从而最大限度地减少锁争用并最大限度地提高性能.
ConcurrentHashMap不会抛出ConcurrentModificationException
如果一个线程试图修改它而另一个线程迭代它,则ConcurrentHashMap不会抛出ConcurrentModificationException
synchornizedMap和ConcurrentHashMap之间的区别
Collections.synchornizedMap(HashMap)将返回一个几乎等同于Hashtable的集合,其中Map上的每个修改操作都锁定在Map对象上,而在ConcurrentHashMap的情况下,通过将整个Map划分为基于并发级别的不同分区来实现线程安全性并且只锁定特定部分而不是锁定整个Map.
ConcurrentHashMap不允许null键或null值,而synchronized HashMap允许一个null键.
类似的链接
链接1
LINK2
绩效比较
你是对的HashTable
,你可以忘掉它.
您的文章提到了这样一个事实:虽然HashTable和同步包装类通过一次只允许一个线程访问映射来提供基本的线程安全性,但这不是"真正的"线程安全性,因为许多复合操作仍需要额外的同步,例:
synchronized (records) { Record rec = records.get(id); if (rec == null) { rec = new Record(id); records.put(id, rec); } return rec; }
但是,不要认为ConcurrentHashMap
对于HashMap
具有synchronized
如上所示的典型块的a而言,这是一个简单的替代方案.阅读此文章,了解其复杂性更好.
这里有几个:
1)ConcurrentHashMap仅锁定Map的一部分,但SynchronizedMap锁定整个MAp.
2)ConcurrentHashMap比SynchronizedMap具有更好的性能,并且可扩展性更强.
3)如果是多个读者和单个编写者,ConcurrentHashMap是最佳选择.
本文来自ConcurrentHashMap与Java中的哈希表之间的区别
我们可以使用ConcurrentHashMap和synchronisedHashmap和Hashtable来实现线程安全.但是,如果你看一下他们的架构,会有很多不同.
synchronisedHashmap和Hashtable
两者都将保持对象级别的锁定.所以如果你想执行像put/get这样的任何操作,那么你必须首先获得锁.同时,不允许其他线程执行任何操作.所以一次只有一个线程可以对此进行操作.所以等待时间会增加.我们可以说与ConcurrentHashMap比较时性能相对较低.
的ConcurrentHashMap
它将保持段级别的锁定.它有16个段,默认情况下将并发级别维持在16.所以一次,16个线程可以在ConcurrentHashMap上运行.而且,读操作不需要锁定.因此任意数量的线程都可以对其执行get操作.
如果thread1想要在段2中执行put操作并且thread2想要在段4上执行put操作,那么这里允许它.意味着,16个线程可以一次对ConcurrentHashMap执行更新(put/delete)操作.
这样等待时间会减少.因此,性能相对于synchronisedHashmap和Hashtable相对更好.
的ConcurrentHashMap
在项目中需要非常高的并发性时,应该使用ConcurrentHashMap.
没有同步整个地图是线程安全的.
使用锁完成写操作时,读操作可以非常快.
对象级别没有锁定.
在hashmap桶级别,锁定的粒度更精细.
如果一个线程尝试修改它而另一个线程迭代它,则ConcurrentHashMap不会抛出ConcurrentModificationException.
ConcurrentHashMap使用多种锁.
SynchronizedHashMap
对象级别的同步.
每次读/写操作都需要获取锁.
锁定整个集合是一种性能开销.
这实际上只允许访问整个映射的一个线程并阻止所有其他线程.
它可能会引起争用.
SynchronizedHashMap返回Iterator,它在并发修改时失败.
资源