< prev index next >

src/java.base/share/classes/java/util/Properties.java

Print this page
rev 50605 : 8199435: Unsafe publication of java.util.Properties.map
Reviewed-by: dholmes, psandoz, plevart, redestad

@@ -146,19 +146,19 @@
      * A property list that contains default values for any keys not
      * found in this property list.
      *
      * @serial
      */
-    protected Properties defaults;
+    protected volatile Properties defaults;
 
     /**
      * Properties does not store values in its inherited Hashtable, but instead
      * in an internal ConcurrentHashMap.  Synchronization is omitted from
      * simple read operations.  Writes and bulk operations remain synchronized,
      * as in Hashtable.
      */
-    private transient ConcurrentHashMap<Object, Object> map;
+    private transient volatile ConcurrentHashMap<Object, Object> map;
 
     /**
      * Creates an empty property list with no default values.
      *
      * @implNote The initial capacity of a {@code Properties} object created

@@ -1095,11 +1095,12 @@
      * @see     #defaults
      */
     public String getProperty(String key) {
         Object oval = map.get(key);
         String sval = (oval instanceof String) ? (String)oval : null;
-        return ((sval == null) && (defaults != null)) ? defaults.getProperty(key) : sval;
+        Properties defaults;
+        return ((sval == null) && ((defaults = this.defaults) != null)) ? defaults.getProperty(key) : sval;
     }
 
     /**
      * Searches for the property with the specified key in this property list.
      * If the key is not found in this property list, the default property list,

@@ -1481,10 +1482,11 @@
     // Hashtable serialization overrides
     // (these should emit and consume Hashtable-compatible stream)
 
     @Override
     void writeHashtable(ObjectOutputStream s) throws IOException {
+        var map = this.map;
         List<Object> entryStack = new ArrayList<>(map.size() * 2); // an estimate
 
         for (Map.Entry<Object, Object> entry : map.entrySet()) {
             entryStack.add(entry.getValue());
             entryStack.add(entry.getKey());

@@ -1535,15 +1537,16 @@
         // what is actually created.
         SharedSecrets.getJavaObjectInputStreamAccess()
                      .checkArray(s, Map.Entry[].class, HashMap.tableSizeFor((int)(elements / 0.75)));
 
         // create CHM of appropriate capacity
-        map = new ConcurrentHashMap<>(elements);
+        var map = new ConcurrentHashMap<>(elements);
 
         // Read all the key/value objects
         for (; elements > 0; elements--) {
             Object key = s.readObject();
             Object value = s.readObject();
             map.put(key, value);
         }
+        this.map = map;
     }
 }
< prev index next >