Переменная Java 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, и в то время как запрос выполняет этот код, может случиться так, что другой запрос напишет новый ключ в свойствах системы Java как простой System.setProperty("test", "foo");

С этим сценарием я получаю ConcurrentModificationException пока я итерирую свойства и другой запрос меняет его.

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

Код, который устанавливает новое свойство, является совершенно другим потоком по сравнению с тем, который выполняет итерацию ключей, но поскольку свойства SystemProperties являются общими для всего экземпляра JVM, я получаю эту ошибку.

Я уже нашел другие темы, относящиеся к этой теме, и наиболее предлагаемое решение - использовать ConcurrentHashMap. Эта опция недоступна для меня, потому что я не тот, кто создает карту SystemProperties, но я только читаю / пишу ее.

Чтобы воспроизвести проблему, вы можете просто добавить setProperty вызовите прямо перед закрывающей скобкой цикла итератора

Какая практика здесь предлагается для решения проблемы?

3 ответа

В этом сценарии я получаю исключение 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 кажется мне лучше.

Я считаю, что вы должны создать объект блокировки и использовать synchronized блокировать всякий раз, когда вы пытаетесь читать / писать в свойствах объекта.

public final Object systemPropLockObject = new Object();

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

Вы не можете изменить список во время итерации.

Два варианта:

  • Используйте пакет java.util.concurrent, чтобы избежать этого исключения ( http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/package-summary.html)
  • ЧИТАЙТЕ, затем НАПИШИТЕ (не одновременно). Сначала выполните итерацию, чтобы составить список. Затем внесите изменения после завершения цикла.

Попробуйте это с 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;
Другие вопросы по тегам