Java属性变量:迭代键时设置属性

时间:2017-04-10 14:27:24

标签: java properties concurrentmodification system-properties

我有一个代码,它收集以给定前缀

开头的所有系统属性键
private static List<String> getKeysByPrefix(String prefix) {
    Set<?> keySet = System.getProperties().keySet();
    Iterator<?> iterator = keySet.iterator();

    List<String> list = new ArrayList<>();
    while (iterator.hasNext()) {
        String key = (String) iterator.next();

        if (key.startsWith(prefix)) {
            list.add(key);
        }
    }

    return list;
}

此代码在Jetty Web服务器内运行,当请求执行此代码时,可能会发生另一个请求在java System属性中写入一个新密钥,如简单System.setProperty("test", "foo");

在这个场景中,我正在迭代属性时获得ConcurrentModificationException,而另一个请求会更改它。

java.util.ConcurrentModificationException
    at java.util.Hashtable$Enumerator.next(Hashtable.java:1378)

设置新属性的代码完全是另一个流程,而不是迭代密钥,但因为SystemProperties在所有JVM实例之间共享,所以我收到了这个错误。

我已经找到了其他主题来处理这个主题,最常见的解决方案是使用ConcurrentHashMap。这个选项对我来说不可用,因为我不是那个创建SystemProperties地图的人,但我只是在读/写它。

为了重现该问题,您只需在迭代器循环的右括号之前添加setProperty调用

这是建议的做法,以解决问题?

3 个答案:

答案 0 :(得分:0)

迭代时无法更改列表。

两个选项:

使用CopyOnWriteArraySet尝试此操作:

    Set<?> keySet = new CopyOnWriteArraySet<Object>(System.getProperties().keySet());
    Iterator<?> iterator = keySet.iterator();

    List<String> list = new ArrayList<String>();
    while (iterator.hasNext()) {
        String key = (String) iterator.next();

        if (key.startsWith(prefix)) {
            list.add(key);
        }
    }

    return list;

答案 1 :(得分:0)

我相信你应该在尝试读取/写入属性对象时创建一个锁对象并使用synchronized块。

public final Object systemPropLockObject = new Object();

synchronized ( systemPropLockObject ) {
    // write the code to read/write to the system properties
}

答案 2 :(得分:0)

  

在这种情况下,我在获取ConcurrentModificationException时正在迭代属性而另一个请求会更改它。

我认为此错误试图告诉您,您正在不恰当地使用System属性。即使它们由HashTable支持,它们也不会被设计为同时迭代和更新。

在查看HashTable代码时,我看到了很多评论:

  

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

毋庸置疑,这并不好。

  

我已经找到了其他主题来处理这个主题,最常见的解决方案是使用ConcurrentHashMap。这个选项对我来说不可用,因为我不是那个创建SystemProperties地图的人,而是我只读/写它。

您是否有一个共享类,它使用初始系统属性映射的内容初始化CHM,但然后读取/写入CHM?

如果您在不谈论属性的情况下编辑问题并谈论自己的需求,那么我们可以提出更好的答案。

最后,如果你绝望了,你的迭代周围的锁可以工作:

Properties props = System.getProperties();
List<String> list = new ArrayList<>();
synchronized (props) {
    // get the properties which match with an enumeration inside the lock
    ...
}

这取决于系统属性如何锁定自身,但这可能对您有用。使用CHM实现不同的解决方案对我来说似乎更好。