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