在多线程应用程序中,是否有任何方案可以在需要时使用HashMap并使用同步,而不是使用ConcurrentHashMap?
具体来说,我正在考虑一个地图初始化的应用程序:
Map<String,String> map = new HashMap<>();
每个线程只访问映射以更新它并立即将其转换为字符串,因此唯一的同步块是:
synchronized(map) {
map.put(key,value);
StringBuilder sb = new StringBuilder();
for (String k: map.keySet())
sb.append("("+k+","+map.get(k)+")");
}
在这种情况下,将初始化更改为:
会更安全Map<String,String> map = new ConcurrentHashMap<>();
并删除“同步”?
答案 0 :(得分:3)
课程文档说明了这一点:
对于诸如putAll和clear之类的聚合操作,并发检索可能反映仅插入或删除某些条目。类似地,Iterators,Spliterators和Enumerations在迭代器/枚举的创建时或之后的某个时刻返回反映哈希表状态的元素。
因此,如果您以原子方式关注这些情况,那么外部同步可能就是您所需要的。
似乎你确实关心这个问题,因为你做keySet()
,这不是原子的,在你做for循环时可能会添加或删除其他条目
答案 1 :(得分:2)
不确定。如果您的使用模式仅在极少数情况下需要同步,则HashMap
的开销将小于ConcurrentHashMap
。这是你需要测量的东西。
当然,除非你正在做一些非常具体和性能密集的事情,否则你在性能方面使用哪一个并不重要。但是对于ConcurrentHashMap
,您不必担心自己执行synchronization
,这样就不会出错。
一个更直接的问题是,是否有任何理由使用Collections.synchronizedMap(new HashMap<>())
代替ConcurrentHashMap
,这已经在很多地方讨论过:ConcurrentHashMap vs Synchronized HashMap
答案 2 :(得分:2)
问题是对应用程序状态需求的重要性。如果你希望在put方法调用转换之后将map的字符串转换与state同步,那么你的代码只是解决方案。
如果我们说你之前的地图状态同步阻止它:
key1:value1
key2:value2
然后你调用
put("key3","value3")
synchronized block确保您的字符串转换为
(key1,value1)(key2,value2)(key3,value3)
如果删除synchronized块并将map更改为某个同步实现,则只有同步部分才会自动执行。因此,方法调用的时间轴在某些情况下可能是: Thread1 put(x,x) Thread2 put(x,x) Thread1用于循环到字符串的转换 Thread2用于循环到字符串转换
因此,Thread1转换错误状态,因为Thread2足够快,可以在Thread1调用字符串转换之前放入一些新条目。
通常,只有在您希望将集合方法作为原子操作的情况下,任何集合的同步实现才有用。如果你需要在相同的收集状态下合作更多方法,你必须每次都在外面同步它们。
答案 3 :(得分:0)
有趣的是,concurrentHashMap的性能非常接近非线程安全的HashMap,并且比同步的HasHmap好得多。因此,我不认为同步HasHmap在一般情况下会更好。 https://dzone.com/articles/java-7-hashmap-vs