简体   繁体   English

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

[英]How can I iterate over (system) properties in a thread safe manner?

The following piece starts throwing ConcurrentModificationException in my application (in the iterator):以下部分开始在我的应用程序中(在迭代器中)抛出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)
}

I am running a multi-threaded application, and unfortunately I do not have access to the code that is modifying the system properties (it could even be a third party library).我正在运行一个多线程应用程序,不幸的是我无法访问修改系统属性的代码(它甚至可能是第三方库)。

To fix that, we can take a snapshot of system property keys:为了解决这个问题,我们可以拍摄系统属性键的快照

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!
}

Notice the value comment - while stringPropertyNames states set of keys in this property list where the key and its corresponding value are strings , the system property could have been changed in the meantime.请注意value注释 - 虽然stringPropertyNames声明set of keys in this property list where the key and its corresponding value are strings ,但同时系统属性可能已更改。


Why so much legwork?怎么跑这么多腿?

System properties are an instance of java.util.Properties , and its methods getProperty , setProperty are thread-safe.系统属性是java.util.Properties一个实例,其方法getPropertysetProperty是线程安全的。

Unfortunately, Properties' entry set's iterator (which I had used in question) is not thread-safe:不幸的是,属性的​​条目集的迭代器(我曾经使用过)不是线程安全的:

If the map is modified while an iteration over the set is in progress (except through the iterator's own remove operation, or through the setValue operation on a map entry returned by the iterator) the results of the iteration are undefined如果在对集合进行迭代时修改了映射(除了通过迭代器自己的删除操作,或者通过迭代器返回的映射条目上的 setValue 操作),迭代的结果是未定义的

So actually when I was iterating over that map, some system property was modified (= that entry said was modified), which caused CME to be thrown.所以实际上当我迭代那个地图时,一些系统属性被修改(=那个条目被修改),这导致 CME 被抛出。


This q&a pair also applies to any generic Properties usage - just system properties make it trickier, with ability to access them directly with statics such as java.lang.System.setProperty(String, String) - so controlling all accesses (especially in shared code) gets harder.这个问答对也适用于任何通用的Properties使用 - 只是系统属性使它变得更棘手,能够使用静态变量直接访问它们,例如java.lang.System.setProperty(String, String) - 因此控制所有访问(尤其是在共享代码中) ) 变得更难。

You could wrap your properties in a ConcurrentHashMap so that any of your compound actions such as iteration, navigation, check-and-act, etc.. will be thread-safe.您可以将您的属性包装在ConcurrentHashMap以便您的任何复合操作(例如迭代、导航、检查和操作等)都是线程安全的。 eg例如

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());
}

Note that the iterators returned by the ConcurrentHashMap are weekly consistent , which means it may or may not reflect the changes to the collection after the construction of the iterator.请注意, ConcurrentHashMap返回的迭代器是每周一致的,这意味着它可能会也可能不会反映迭代器构建后对集合的更改。 If this is not what you wanted, you could use the Collections.synchronizedMap() instead, which pays some penalties in concurrency.如果这不是你想要的,你可以使用Collections.synchronizedMap()代替,它会在并发方面付出一些代价。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM