如何以线程安全的方式遍历(系统)属性?

时间:2018-09-21 15:36:00

标签: java concurrency system-properties

以下内容开始在我的应用程序(在迭代器中)中抛出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)
}

我正在运行一个多线程应用程序,但是不幸的是,我无法访问修改系统属性的代码(它甚至可能是第三方库)。

2 个答案:

答案 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的实例,其方法getPropertysetProperty是线程安全的。

不幸的是,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()来代替,这会导致并发性的损失。