Java programming language logo

Guarded Block

There are situations where threads should not proceed until a certain condition come to fruition. For example, a thread has to wait for a variable to be set to true before it is allowed to proceed. This is when the guarded block is applied. In the guarded block, the thread keeps checking a condition that must come to true before the thread proceeds the execution.

This can be achieved in two ways. First is the continuous looping, which is not effective at all.


// Don’t do this.
public void awfulGuardedBlock() {
	while (!flag);
	System.out.println("The flag is true now.");
}	

This solution wastes processor time because it loops continuously until the flag becomes to true by another thread. Java provides a much better solution with the Object.wait method.


public synchronized void guardedBlock() {
	while (!flag) {
		try {
			wait();
		} catch (InterruptedException e) {}
	}
	System.out.println("The flag is true now.");
}

In this solution, the wait method is called, which suspends the thread until another thread invokes the Object.notify or the Object.notifyAll method. This technique uses synchronization because the thread has to own the intrinsic lock of the object whose wait method is called, and after the wait, the thread releases the lock and suspend itself. This makes the CPU available for other threads. After that, another thread sets the flag to true and calls one of the notify methods on the same lock object. This wakes the previous thread up and makes it to proceed, but before that, the thread has to get the lock again.

The below method takes place in the same object where wait was called, and also synchronized, so it uses the same intrinsic lock.


public synchronized setFlag() {
	flag = true;
	notifyAll();
}

What is the difference between the notify and notifyAll methods? The notify method wakes up an arbitrary, single thread that is waiting on the same intrinsic lock. If multiple threads are waiting, only one of them gets woken up. The notifyAll method wakes up all threads those are waiting on the same intrinsic lock. So notify method can be used when there is only one waiting thread, or it doesn’t matter which one gets woken up.

The the wait is called in a while loop because the thread maybe gets woken up, but the condition can still be false, so it has to call the wait again.

Ping-Pong Example

In the next example, a ping-pong game is played by two threads. It means the Ping and Pong words are printed out one after another a few times. The threads are the players, and one of them prints out the Ping word, the other the Pong word. When one of the threads printed a word, it has to wait for the other thread to print the other word before printing again.

The PingPong class has two methods. Both of them are synchronized and contain a guarded block. One thread keeps invoking the ping method, the other the pong method.

When the game starts, the ping method gets invoked from the first thread (see the TestPingPong class below). The isPongComming field is false, so the while loop is skipped, the field is set to true, notify is called and the Ping word is printed out. After that, the thread calls this method again, but this time the isPongComming field is true, so the wait gets called, which suspends the thread and releases the lock of this instance.

After this, the second thread can proceed, which calls the pong method. The isPongComming field is true, so the while loop is skipped, the field is set to false, notify is called and the Pong word is printed out.

Calling the notify method wakes up an arbitrary thread. Because there is only one waiting thread in this example, we know exactly which one will be. If we had four player, like in the next example, the notifyAll method should be invoked here. In this case, it doesn’t matter whether the notify or the notifyAll method gets called.

The notify method wakes up the first thread, but that cannot proceed while the pong method running because these methods are synchronized, so the second thread keeps running and calling the pong method again. This time the isPongComming field is false and the wait method gets executed, which suspends the second thread and lets the first tread proceed.

The first thread continues after the ping method’s wait(); line, and the previous steps come again and again.


package com.programcodex.concurrency.waitnotify.pingpong;

public class PingPong {

    private boolean isPongComming;

    public synchronized void ping() {
        while (isPongComming) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        isPongComming = true;
        notify();

        System.out.println("Ping");
    }

    public synchronized void pong() {
        while (!isPongComming) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        isPongComming = false;
        notify();

        System.out.println("    Pong");
    }
}

The TestPingPong class instantiates a PingPong class and shares it between two threads. The ping thread invokes the ping method of the pingPing object five times. The pong thread invokes the pong method five times.


package com.programcodex.concurrency.waitnotify.pingpong;

import java.util.stream.IntStream;

public class TestPingPong {

    public static void main(String[] args) {
        System.out.println("Ping-Pong with two players\n");

        PingPong pingPong = new PingPong();

        Thread ping = new Thread(() -> {
            IntStream.range(0, 5)
            .forEach(i -> pingPong.ping());
        });
        
        Thread pong = new Thread(() -> {
            IntStream.range(0, 5)
            .forEach(i -> pingPong.pong());
        });

        ping.start();
        pong.start();
    }
}

The output of the above class:

wait, notify, notifyAll

Ping-Pong with Four Players

In the below example, the ping-pong is played by four threads. They print out the Ping, Pong, PING and PONG words. Now the PingPong class has only a single play method and all threads call this.

The most important difference to the previous version is that the notify method replaced with the notifyAll because now the notify could stop the execution of the threads. If not the proper thread gets notified, it would call the wait method again, which suspends the thread, and no one would call any notify method. All thread would be suspended forever. The notifyAll ensures that all thread get notified, and one of them will proceed.

In the PingPong class, the players array contains the thread names of the players, and the idx field holds the index of the current player in the array.

The while loop in play method checks the name of the current thread. If it is equal with the current player, the loop is skipped, a word is printed out, the index is set to the next player, and all threads get notified. After that, this thread calls the play method again, but this time the wait method executed, which suspends the thread and releases the lock.

There are three woken up threads. One of them gets the chance to continue. It checks the loop’s condition again, which can still be true, so the wait gets called again. This means, not this thread supposed to come. Another thread can continue, and if this is the proper thread, it takes the above mentioned steps and so on.


package com.programcodex.concurrency.waitnotify.pingpong4;

public class PingPong {

    private String[] players = { "Ping", "Pong", "PING", "PONG" };
    private int idx = 0;

    public synchronized void play() {
        while (!Thread.currentThread().getName().equals(players[idx])) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.format("%" + (4 + idx * 4) + "s%n", players[idx]);

        setNext();
        notifyAll();
    }

    private void setNext() {
        if (++idx > players.length - 1) {
            idx = 0;
        }
    }
}

The Player is a Runnable class. It receives a PingPong instance in its constructor and invokes the play method of that instance three times.


package com.programcodex.concurrency.waitnotify.pingpong4;

public class Player implements Runnable {

    private PingPong pingPong;

    public Player(PingPong pingPong) {
        this.pingPong = pingPong;
    }

    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            pingPong.play();
        }
    }
}

The TestPingPong class instantiates the PingPong and a Player class and starts four threads with them. The four thread share the pingPong object, and each thread instantiated with different names. These names are checked in the play method of the PingPong class.


package com.programcodex.concurrency.waitnotify.pingpong4;

public class TestPingPong {

    public static void main(String[] args) {
        System.out.println("Ping-Pong with four players\n");

        PingPong pingPong = new PingPong();
        Runnable player = new Player(pingPong);

        new Thread(player, "Ping").start();
        new Thread(player, "Pong").start();
        new Thread(player, "PING").start();
        new Thread(player, "PONG").start();
    }
}

The output of the above class:

Guarded Block