Search code examples
javamultithreadingconcurrencywaitnotify

How do I make a Thread wake up last on notify?


I have multiple threads waiting on a notify() from a tick clock. One of these threads should wait for the rest to execute before it runs. Normally I believe the way to do this would be to use a join() but in this case the Threads never die, they just wait() for the next tick signal. Is there a way to ensure that Thread "z" will always wake up after Threads "a-y" upon receiving the same notify()?

EDIT: Added code for example

Thread 1-4:

while(running) {
    synchronized(tickSignal){
        /*
         * Code in this section adds objects to a queue that Thread 5 reads
         * It also has other code that must be executed every tick
         */
        tickSignal.wait();
    }
}

Thread 5:

while(running) {
    synchronized(tickSignal) {
        /*
         * Code in this section reads all the objects added to the queue by T1-4
         * It also has other code that must be executed every tick
         */
        tickSignal.wait();
    }
}

Tick Clock:

while(running) { 
    synchronized(tickSignal){
        tickSignal.notifyAll();
    }
    Thread.sleep(1000);
}

There are also other threads monitoring tickSignal that do not interact with Thread 5 at all.


Solution

  • If I understand it correctly, there are N tasks to be executed when a tick signal is given. The N-th task can only start after the first N-1 tasks have been completed. Since the notifyAll() function notifies Threads in an unordered way, you have to extend your code slightly.

    First of all, I think this construction is not a safe construction. Think about the case that executing the code in a Thread takes longer than 1 second. In that case, the Thread will not be notified at the next tick signal, since it did not yet reach the wait() function. However, let's for the moment assume that this will not occur.

    Since the N-th task can only be executed after the first N-1 tasks are completed, it has to wait and has to be notified when the first N-1 tasks are actually completed. In order to count the number of completed tasks, you can use a Thread-safe AtomicInteger counter. Each time a task is completed, the counter increases with 1. When the counter reaches the value N-1, it notifies the N-th Thread and the value is reset to 0.

    To give you the code:

    // Besides a tickSignal, we also need a finalThreadSignal, which 
    // will be notified when the first N-1 Threads are finished.
    private Object tickSignal = new Object();
    private Object finalThreadSignal = new Object();
    private AtomicInteger completedThreadsCounter = new AtomicInteger(0);
    

    Thread 1-(N-1):

    while (running) {
        synchronized (tickSignal) {
            try {
               tickSignal.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // Code
    
            // Task finished
            int counter = completedThreadsCounter.incrementAndGet();
            if (counter == N-1) {
                // Notify Thread N when the first N-1 tasks are finished
                synchronized (finalThreadSignal) {
                    finalThreadSignal.notify();
                }
                // Reset the finished Threads counter and wait for the next tick signal
                completedThreadsCounter.set(0);
            }
        }
    }
    

    Thread N:

    while (running) {
        // Here we synchronize on the signal that will be given when 
        // the first N-1 Threads are finished
        synchronized (finalThreadSignal) {
            try {
                finalThreadSignal.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // Execute final Thread code
        }
    }
    

    As I already indicated, this construction will fail if the execution time in a Thread is larger than the time between two ticks. Please let me know exactly what the problem is to give you are more suitable answer.