javaconcurrencyconcurrenthashmap

Does Java 11 Properties class uses double synchronization? It uses ConcurrentHashMap and at the same time synchronized methods


I'm currently diving into the implementation of the Java 11 Properties class. In Java 11 source code, the Properties class stores its data in a ConcurrentHashMap field called map. When you call the method setProperty of Properties, under the hood it makes a put method call of its internal ConcurrentHashMap. I understand that ConcurrentHashMap is a thread-safe collection, so I don't really get why the setProperty method has to be synchronized if ConcurrentHashMap already is. The synchronization of the setProperty method isn't killing the point of using a ConcurrentHashMap. By this, I mean that users of the Properties class will have to wait to lock for the setProperty method that already uses a concurrent collection with its own synchronization.

public class Properties extends Hashtable<Object,Object> {
/**
 * use serialVersionUID from JDK 1.1.X for interoperability
 */
private static final long serialVersionUID = 4112578634029874840L;

private static final Unsafe UNSAFE = Unsafe.getUnsafe();

/**
 * A property list that contains default values for any keys not
 * found in this property list.
 *
 * @serial
 */
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 volatile ConcurrentHashMap<Object, Object> map;
public synchronized Object setProperty(String key, String value) {
    return put(key, value);
}
@Override
public synchronized Object put(Object key, Object value) {
    return map.put(key, value);
}

Note this is not the complete source code of Properties. The complete implementation can be found here: https://github.com/AdoptOpenJDK/openjdk-jdk11/blob/master/src/java.base/share/classes/java/util/Properties.java


Solution

  • It's because of backward compatibility. In Java 8, Properties didn't delegate to a ConcurrentHashMap yet but instead relied on it extending Hashtable. That used synchronized method, and therefore so did Properties. That has one big impact - the locking can be used externally. For instance, to use a synchronized block with a Properties instance as lock, and perform all kinds of operations inside it.

    If the existing methods are no longer synchronized, then it becomes possible to update the Properties instance outside of such synchronized blocks, possibly breaking existing code.