如何以线程安全的方式遍历(系统)属性?
How can I iterate over (system) properties in a thread safe manner?
以下片段开始在我的应用程序(在迭代器中)中抛出 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)
}
我是运行一个多线程应用程序,不幸的是我无法访问修改系统属性的代码(它甚至可能是第三方库)。
要解决这个问题,我们可以拍摄系统 属性 键的 快照 :
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 的条目集的迭代器(我曾在问题中使用过)不是线程安全的:
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
所以实际上当我迭代那个地图时,一些系统 属性 被修改了(= 那个条目说被修改了),这导致 CME 被抛出。
这个问答对也适用于任何通用的 Properties
用法 - 只是系统属性让它变得更棘手,能够直接使用诸如 java.lang.System.setProperty(String, String)
之类的静态访问它们 - 所以控制所有访问(特别是在共享代码)变得更难。
您可以将您的属性包装在 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()
,这会在并发性方面造成一些损失。
以下片段开始在我的应用程序(在迭代器中)中抛出 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)
}
我是运行一个多线程应用程序,不幸的是我无法访问修改系统属性的代码(它甚至可能是第三方库)。
要解决这个问题,我们可以拍摄系统 属性 键的 快照 :
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 的条目集的迭代器(我曾在问题中使用过)不是线程安全的:
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
所以实际上当我迭代那个地图时,一些系统 属性 被修改了(= 那个条目说被修改了),这导致 CME 被抛出。
这个问答对也适用于任何通用的 Properties
用法 - 只是系统属性让它变得更棘手,能够直接使用诸如 java.lang.System.setProperty(String, String)
之类的静态访问它们 - 所以控制所有访问(特别是在共享代码)变得更难。
您可以将您的属性包装在 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()
,这会在并发性方面造成一些损失。