If Java Were Designed Today: The Synchronizable Interface

Java has come a long way. A very long way. And it carries with it all the “junk” from early day design decisions. One thing that has been regretted time and again is the fact that every object (potentially) contains a monitor. This is hardly ever necessary and this flaw was corrected, finally, in Java 5, when new concurrency APIs were introduced, such as the java.util.concurrent.locks.Lock and its subtypes. Since then, writing synchronized, concurrent code has become a lot easier than before when we only had the synchronized keyword and the hard-to-understand wait() and notify() mechanism:

The synchronized modifier is hardly used anymore

The original language design specified for these “convenience” modifiers on methods:

// These are the same, semantically:
public synchronized void method() {
    ...
}

public void method() {
    synchronized (this) {
        ...
    }
}

// So are these:
public static synchronized void method() {
    ...
}

public static void method() {
    synchronized (ClassOfMethod.class) {
        ...
    }
}

(note, while the byte code produced above is not the same, the high level semantics certainly is) You hardly want to synchronize on the complete method scope, in order to keep synchronization time at a minimum, and factoring out a method every time you need synchronization is tedious. Furthermore, the monitor breaks encapsulation. Everyone can synchronize on your monitor if you synchronize on this or on the entire class. You probably don’t want that, which is why most people who still do work with the synchronized keyword will simply create an explicit, private lock object, such as:

class SomeClass {
    private Object LOCK = new Object();

    public void method() {
        ...

        synchronized (LOCK) {
            ...
        }

        ...
    }
}

If that’s the standard use-case for classic synchronized blocks, do we then still need a monitor on every object?

Synchronized in a more modern Java version

If Java were designed with today’s knowledge about the Java language, we wouldn’t allow for using synchronized on any random object (including strings or arrays):

// Wouldn't work
synchronized ("abc") {
    ...
}

We would introduce a special Synchronizable marker interface, which guarantees that implementors will have a monitor. And the synchronized block would only accept Synchronizable arguments:

Synchronizable lock = ...

synchronized (lock) {
    ...
}

This would work exactly the same way as foreach or try-with-resources:

Iterable<Object> iterable = ...

// The type to the right of ":" must be Iterable
for (Object o : iterable) {
    ...
}

// The assignment type must be AutoCloseable
try (AutoCloseable closeable = ...) {
    ...
}

// The assignment type must be a functional interface
Runnable runnable = () -> {};

So, in order for a given language feature to work, the Java language imposes constraints on the types that are used in that context. In the case of foreach or try-with-resources, a concrete JDK type is required. In the case of lambda expressions, a matching structural type is required (which is rather esoteric but clever, for Java). Unfortunately, for backwards-compatibility reasons, there will not be any new restriction added for synchronized blocks. Or will there? It would be great, and an optional warning could be issued if the type is not Synchronizable. This might allow, in the course of a couple of future major releases, to remove monitors from objects that are not really required to be synchronizable. Which is essentially what the C language has been doing with mutexes all along. They’re a special thing. Not the common thing.

10 thoughts on “If Java Were Designed Today: The Synchronizable Interface

  1. Is there a way to make JOOQ work in Netbeans . Perhaps there is somewhere an “ho to” but I cannot find out how it should work . I’m new to JOOQ and considering to use it for a new project.

    1. Hmm, no, I hadn’t read that post – seems like an interesting coincidence, then. Indeed, I’m aware of the value type proposal for JDK 10, and it will make these thoughts obsolete, hopefully

  2. “We would introduce a special Synchronizable marker interface”

    Why would it be a marker interface? It should be a final class. What else could you use a lock in addition to lock on it without breaking the single responsibility principle?

  3. Based on our experience with Plumbr lock contention monitoring, indeed, the design of the original synchronization via monitors carries a heavy cost. In almost half of the applications we end up monitoring, the engineering is surprised to see how badly the synchronized code performs due to the way the concurrent aspects were designed in the application.

    It is just so easy to misuse the “synchronized” concept, it is no wonder it happens. Based on the experience we ended up outlining the most common “antipatterns” in https://plumbr.eu/blog/locked-threads/improving-lock-performance-in-java.

    Full disclosure: I am affiliated with Plumbr

Leave a Reply to Peter Verhas Cancel reply