以下内容开始在我的应用程序(在迭代器中)中抛出ConcurrentModificationException
:
final Map<?, ?> systemProperties = System.getProperties()
final Map<String, String> properties = (Map<String, String>) systemProperties
for (final Entry<String, String> entry : properties.entrySet()) { // exception here
System.out.println(entry)
}
我正在运行一个多线程应用程序,但是不幸的是,我无法访问修改系统属性的代码(它甚至可能是第三方库)。
答案 0 :(得分:1)
要解决此问题,我们可以获取系统属性键的快照:
final Properties systemProperties = System.getProperties()
final Set<String> keys = systemProperties.stringPropertyNames()
for (final String key : keys) {
System.out.println("key: " + key)
final String value = systemProperties.getProperty(key)
System.out.println("value: " + value) // value can be null!
}
请注意value
注释-当stringPropertyNames
声明set of keys in this property list where the key and its corresponding value are strings
时,系统属性可能已同时更改。
为什么那么多腿部动作?
系统属性是java.util.Properties
的实例,其方法getProperty
,setProperty
是线程安全的。
不幸的是,Properties的条目集的迭代器(我曾经使用过)不是线程安全的:
如果在对集合进行迭代时修改了地图(通过迭代器自己的remove操作或通过迭代器返回的映射条目上的setValue操作除外),则迭代的结果是不确定的
因此,实际上,当我遍历该映射时,某些系统属性被修改(=所述条目被修改),这导致CME被抛出。
此问与答对也适用于任何通用Properties
用法-只是系统属性使之更加棘手,可以直接使用诸如java.lang.System.setProperty(String, String)
之类的静态变量来访问它们-因此可以控制所有访问(尤其是在共享代码中) )变得越来越困难。
答案 1 :(得分:0)
您可以将属性包装在ConcurrentHashMap
中,以便您的任何复合操作(例如迭代,导航,检查和操作等)都是线程安全的。
例如
ConcurrentHashMap<String, String> props = new ConcurrentHashMap<>(
(Map<String, String>)(Object)System.getProperties());
for(Map.Entry<String, String> entry: props.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
请注意,ConcurrentHashMap
返回的迭代器是每周一致的,这意味着它可能反映也可能不反映构造迭代器后对集合的更改。如果这不是您想要的,则可以使用Collections.synchronizedMap()
来代替,这会导致并发性的损失。