java.util.ConcurrentModificationException при чтении из файла свойств - PullRequest
0 голосов
/ 23 октября 2018

У меня проблемы с чтением свойств конфигурации JVM с System.getProperties() из-за того, что в некоторых средах я получаю java.util.ConcurrentModificationException после перезапуска JVM.

[err] java.util.ConcurrentModificationException [err] в java.util.Hashtable $ Enumerator.next (Hashtable.java:1502)

Мне нужно перезапуститьнесколько раз VM, чтобы избежать ранее упомянутой ошибки.

Что я делал до сих пор:

  • синхронизировал метод, где ключи свойств и значения читаются и записываются в файл журнала;
  • , поскольку объект свойств Java является HashMap, я пытался использовать синхронизированную карту;
  • Я пытался смоделировать проблему параллелизма, используя больше потоков, которые совместно используют один и тот же объект свойств, однако у меня естьне удалось воспроизвести ошибку в моем локальном окружении;
  • Я использую StringBuffer из-за того, что он потокобезопасен;

    private static final Logger LOG = LoggerFactory.getLogger(PropertyLogger.class);
    public static synchronized final void logProperties() {
            Level oldLevel = LOG.getLevel();
            LOG.setLevel(Level.INFO);
            Properties props = System.getProperties();
            Map<Object, Object> shared = Collections.synchronizedMap(new HashMap<>());
            shared.putAll(props);
    
            for (final Entry<Object, Object> entry : shared.entrySet()) {
              try{  StringBuffer buffer = new StringBuffer();
                buffer.append(entry.getKey());
                buffer.append(" = "); //$NON-NLS-1$
                buffer.append(entry.getValue());
                LOG.info(buffer.toString());
              }
              catch (Exception ex){
                  ex.printStackTrace();
              }
            }
            LOG.setLevel(oldLevel);
    
        }
    

Возможно, проблемы, с которыми я сталкиваюсь, запускаются также библиотекой log4j, используемой для записи, которая, насколько я знаю, не безопасна для потоков.

Я добавил трассировку стека:

[err] java.util.ConcurrentModificationException [err] в java.util.Hashtable $ Enumerator.next (Hashtable.java:1502) [err] в com.myapp.common.PropertyLogger.logProperties (PropertyLogger.java: 23) [err] в com.myapp.input.ConfigurationServer.parse (ConfigurationServer.java:710) [err] в com.myapp.input.ConfigurationServer.configure (ConfigurationServer.java:94) [err] в com.myapp.input.Application.run (Application.java:78) [err] at com.myapp.input.servlet.InputStarter $ ValidatorThread.run (InputStarter.java:113)

С уважением,

PS: Должен ли я добавить синхронизированные блоки для вызова метода?Например: метод parse должен вызывать мой код с

synchronized(this) {
     PropertyLogger.logProperties();
}

1 Ответ

0 голосов
/ 23 октября 2018

Проблема не в каркасе ведения журнала, а в строке

shared.putAll(props);

Properties класс расширяет Hashtable, и свойства системы могут измениться в любое время.С shared.putAll(props) вы перебираете объект props класса Properties(Hashtable), и если какое-либо значение будет изменено во время итерации, мы получим эту ошибку ConcurrentModificationException

Решением будет вызов клона() для объекта System.Properties () перед итерацией

...