【问题标题】:How can I iterate over (system) properties in a thread safe manner?如何以线程安全的方式迭代(系统)属性?
【发布时间】:2018-09-21 15:36:00
【问题描述】:

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

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

【问题讨论】:

    标签: java concurrency system-properties


    【解决方案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 的条目集的迭代器(我曾在问题中使用过)不是线程安全的:

    如果在对集合进行迭代时修改了映射(除了通过迭代器自己的删除操作,或通过对迭代器返回的映射条目的 setValue 操作),迭代的结果是不确定的

    所以实际上,当我在该地图上进行迭代时,一些系统属性被修改(= 表示该条目已被修改),这导致 CME 被抛出。


    这个 q&a 对也适用于任何通用的 Properties 用法 - 只是系统属性使它变得更加棘手,能够使用诸如 java.lang.System.setProperty(String, String) 之类的静态变量直接访问它们 - 因此控制所有访问(尤其是在共享代码中)变得更加困难。

    【讨论】:

    • 你在哪里发现 setProperty()getProperty() 是线程安全的?
    • This class is thread-safe: multiple threads can share a single Properties object without the need for external synchronization
    【解决方案2】:

    您可以将属性包装在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(),这会在并发时付出一些代价。

    【讨论】:

    猜你喜欢
    • 2011-08-05
    • 1970-01-01
    • 1970-01-01
    • 2017-04-15
    • 1970-01-01
    • 1970-01-01
    • 2012-11-07
    • 1970-01-01
    相关资源
    最近更新 更多