How to Effectively Protect Critical Section

Not everywhere could be immutable objects used to ensure thread-safe code.


Sometimes we need to wait for some events (especially coming as user input) to initialize a variable. In the case this input can come from a concurrent environment we need to protect the initialization as critical section.

Native Java approach is to use the keyword synchronized of the critical code and this will work... but is it effective as well?

Let's discuss some more methods how to protect a critical section in the Java code.

As usual we start as simple as possible, this is our code:

class MyCriticalSection {

    static Object toBeInitialized = null;   // mutable static object

    public void callFromClient(Object... globalParams) {
        if (toBeInitialized == null) {
            toBeInitialized = initialize(globalParams);
        }

        // so something with the toBeInitialized object
        System.out.println(toBeInitialized.toString());
    }

    private Object initialize(Object... params) {
        // do some better stuff here...
        return new Object();
    }
}

Completely unsafe. So let's synchronize it:

public synchronized void callFromClient(Object... globalParams) {
    // ...
}

Very ineffective. Actually we want to synchronize only the initialization part:

public void callFromClient(Object... globalParams) {
    synchronized (MyCriticalSection.class) {
        if (toBeInitialized == null) {
            toBeInitialized = initialize(globalParams);
        }
    }
    // ...
}

Synchronizing on the class means a caller trying to access the section will be block even when another caller is inside another section synchronized on the class as well:

void anotherCall() {
    synchronized (MyCriticalSection.class) {
        // ...
    }
}

The same for static methods:

void static doSomething() {
    synchronized (MyCriticalSection.class) {
        // ...
    }
}

This could be okay when the initialization is distributed or static, but even for these cases we have a better option:

private final Object initializationLock = new Object();     // could be static for static methods

public void callFromClient(Object... globalParams) {
    synchronized (initializationLock) {
        if (toBeInitialized == null) {
            toBeInitialized = initialize(globalParams);
        }
    }
    // ...
}

Looks nice, we're done...? Actually not, using the synchronized keyword is far not the most effectively way how to protect a critical section.

Java 5 came with a package of atomic classes java.util.concurrent.atomic among then AtomicBoolen.

AtomicBoolen could be used to check atomically if a condition was already fulfilled or not:

private final AtomicBoolean alreadyInitialized = new AtomicBoolean();

public void callFromClient(Object... globalParams) {
    if (alreadyInitialized.compareAndSet(false, true)) {
        toBeInitialized = initialize(globalParams);
    }
    // ...
}

This works the same way as the synchronized section above and is much more effective... but well, there is another little devil hidden in the code.

Take a look at the code above including the synchronized variants. What happens when another caller calls the method in the same time as the first method entered the critical section to initialize the object?

The second caller doesn't get the access to the critical section and continues in the code execution. But this means he works with a potentially uninitialized object and the last line of code will throw a NullPointerException trying to call toString() method on the null value.

Our job is not done here yet...

In the package java.util.concurrent we can find a classCountDownLatch designed to synchronize concurrency of thread processing:

private final AtomicBoolean initializationStarted = new AtomicBoolean();
private final CountDownLatch initializationFinished = new CountDownLatch(1);

public void callFromClient(Object... globalParams) throws InterruptedException {
    if (initializationStarted.compareAndSet(false, true)) {
        toBeInitialized = initialize(globalParams);
        initializationFinished.countDown();
    } else {
        initializationFinished.await();
    }
    // ...
}

So, what happens here? We use the atomic boolean to check if the object was already initialized or not. If not we access the critical section. Because the set and set of the atomic boolean is done atomically only one thread can access the initialization.

The initialization is first ready when the first caller counts down the latch. Before the latch was count-downed all the concurrent callers wait on its await() method.

All this doesn't look as nice and easy as with the synchronized keyword. So, is it really so effective?

I did performance tests using all the above mentioned techniques. I tried to access the critical section one million times for each of them:

technique execution time
not thread-safe 2 ms
synchronized class 32 ms
synchronized object 26 ms
atomic-latch 16 ms

Of course there a great penalty of synchronization but this shouldn't be a big surprise, I guess.

The more important thing is the AtomicBoolean-CountDownLatch synchronization is over 40 % more effective. In environments like servers where the calls come for every request hundred thousand times in second this is a pretty nice optimization.