Search code examples
javaconcurrencysystem-properties

How can I iterate over (system) properties in a thread safe manner?


The following piece starts throwing ConcurrentModificationException in my application (in the iterator):

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)
}

I am running a multi-threaded application, and unfortunately I do not have access to the code that is modifying the system properties (it could even be a third party library).


Solution

  • To fix that, we can take a snapshot of system property keys:

    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!
    }
    

    Notice the value comment - while stringPropertyNames states set of keys in this property list where the key and its corresponding value are strings, the system property could have been changed in the meantime.


    Why so much legwork?

    System properties are an instance of java.util.Properties, and its methods getProperty, setProperty are thread-safe.

    Unfortunately, Properties' entry set's iterator (which I had used in question) is not thread-safe:

    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

    So actually when I was iterating over that map, some system property was modified (= that entry said was modified), which caused CME to be thrown.


    This q&a pair also applies to any generic Properties usage - just system properties make it trickier, with ability to access them directly with statics such as java.lang.System.setProperty(String, String) - so controlling all accesses (especially in shared code) gets harder.