Collections.synchronizedMap(new LinkedHashMap());没有使 Map 线程安全
Posted
技术标签:
【中文标题】Collections.synchronizedMap(new LinkedHashMap());没有使 Map 线程安全【英文标题】:Collections.synchronizedMap(new LinkedHashMap()); is not making Map threadsafe 【发布时间】:2012-10-20 12:15:54 【问题描述】:我正在使用以下构造来创建线程安全 Map
。
Collections.synchronizedMap(new LinkedHashMap());
虽然我收到了ConcurrentModificationException
错误。
【问题讨论】:
你可以使用线程安全的哈希表 @user1479853HashTable
Java 社区不鼓励使用。
@user1479853 .... 也解决不了问题。
【参考方案1】:
没有代码很难猜出真正的问题是什么,但我的猜测是,您没有使用返回的集合来执行操作。根据javadoc
为了保证串行访问,对支持集合的所有访问都必须通过返回的集合完成。 用户在迭代返回的集合时必须手动同步它:
Collection c = Collections.synchronizedCollection(myCollection);
...
synchronized(c)
Iterator i = c.iterator(); // Must be in the synchronized block
while (i.hasNext())
foo(i.next());
不遵循此建议可能会导致不确定的行为。
【讨论】:
使用ConcurrentHashMap
代替Collections.synchronizedCollection(myCollection);
可以避免该问题。【参考方案2】:
不要贬低这里的任何其他答案,但下面的这段代码表明并发修改与实际的多线程几乎没有关系。这是因为您说迭代集合但在迭代时修改它......
List list = new ArrayList();
list.add("1");
list.add("2");
Iterator i = list.iterator();
while (i.hasNext())
Object value = i.next(); // throws java.util.ConcurrentModificationException
list.add("another");
【讨论】:
【参考方案3】:长话短说,在您的代码中没有得到ConcurrentModificationException
的解决方案是使用ConcurrentHashMap
而不是Collections.synchronizedMap(new LinkedHashMap());
。此处说明:
正如 Nambari 所说,如果没有实际代码,则更难识别问题。请注意,此Map
仅保护包含的对象。不过,您可以在方法中修改相同的对象实例:
Map<String, Object> map = new ConcurrentHashMap<String, Object();
//fill the map...
map.put("data", new Data());
//and now we have this unsynchronized method that two or more threads can access at the same time
public void modifyData(String key, int newDataIntValue)
//this is synchronized by the ConcurrentHashMap
Data data = map.get(key);
//this isn't
//you can get problems here...
data.setIntValue(newDataIntValue);
同步集合不会为这些情况保存您的代码。你应该自己同步这个方法。
附加信息:如果您正在尝试实现缓存库或Flyweight design pattern,请不要重新发明***并使用经过验证和测试的框架,例如ehcache 或jboss cache。
【讨论】:
【参考方案4】:请查找 java 文档
返回由指定映射支持的同步(线程安全)映射。为了保证串行访问,对支持映射的所有访问都必须通过返回的映射来完成。 当迭代任何集合视图时,用户必须在返回的地图上手动同步:
Map m = Collections.synchronizedMap(new HashMap());
...
Set s = m.keySet(); // Needn't be in synchronized block
...
synchronized(m) // Synchronizing on m, not s!
Iterator i = s.iterator(); // Must be in synchronized block
while (i.hasNext())
foo(i.next());
不遵循此建议可能会导致不确定的行为。 如果指定的地图是可序列化的,则返回的地图将是可序列化的。
参数: m 将地图“包装”在同步地图中。 回报: 指定地图的同步视图。
【讨论】:
您的同步不适用于同时访问此方法的 2 个线程。【参考方案5】:Synchronized 与 ConcurrentModificationException 无关,因为如果您在迭代列表时尝试使用列表的 remove 方法删除列表项,它可以在单线程环境中发生。
同步只保证你串行访问。
【讨论】:
以上是关于Collections.synchronizedMap(new LinkedHashMap());没有使 Map 线程安全的主要内容,如果未能解决你的问题,请参考以下文章