javamultithreadingparallel-processingsynchronizationcountdownlatch

CountDownLatch in Java need additional synchronization?


Let's say I have the code below:

public class CountDownLatchExample {  // Note : Exception handling ommited
    void main(){
        CountDownLatch startSignal = new CountDownLatch(1);
        CountDownLatch doneSignal = new CountDownLatch(N_PARTIES);
        for(int i = 0; i < N_PARTIES; i++){ // create and start threads
            new Thread(() -> {
                startSignal.await();    // Wait startSignal before doing Work
                doWork();
                doneSignal.countDown();
            }).start();
        }
        // (...) do something else              // Don't let run yet
        startSignal.countDown();                // Let all threads proceed
        // (...) do something else
        doneSignal.await();                     // Wait for all threads to finish
    }
}

The main thread creates and starts the worker threads. In the run method, the other threads wait until the main thread call startSignal.countDown(), and then they can call doWork() and doneSignal.countDown().

I know that there is an happens-before relationship between the countDown() called by a thread and threads that return from await, then if a thread call doneSignal.countDown(), what it did is visible to all the other threads.

But my question is if the run() method is executed sequentially or not, and if we need to add synchronization??

Because when startSignal.countDown() is called, all the threads can execute the run method, but suppose that in the doWork() method there are some shared variables that change, or also simply that executing run() concurrently, there are maybe three threads that executed doWork() concurrently, and then two of them are scheduled out right before doneSignal.countDown(), and the third one call doneSignal.countDown(). Here the happens-before relationship is a little bit useless if the other two threads have already executed doWork() and they have only to call doneSignal.countDown(), because they didn't see what the third one did, because they executed doWork() "together".


Solution

  • The CountDownLatch does not guarantee per the mutual exclusion of the accesses to the share data, rather this mechanism is used to synchronized -- in the sense of one waiting for the other -- parallel task among each other. Similar to the functionality provided by a Cyclic barrier. Actually, as you have described this is the reason why you are using this mechanism in your code; to coordinate the execution of the main and the remaining threads.

    If the doWork() method contains shared state among threads, which is modified concurrently, then you might have race-condition. Hence, you need to ensure mutual exclusion of that shared using for instance the synchronized clause.