A Simple Scenario Using Wait() and Notify() in Java

A simple scenario using wait() and notify() in java

The wait() and notify() methods are designed to provide a mechanism to allow a thread to block until a specific condition is met. For this I assume you're wanting to write a blocking queue implementation, where you have some fixed size backing-store of elements.

The first thing you have to do is to identify the conditions that you want the methods to wait for. In this case, you will want the put() method to block until there is free space in the store, and you will want the take() method to block until there is some element to return.

public class BlockingQueue<T> {

private Queue<T> queue = new LinkedList<T>();
private int capacity;

public BlockingQueue(int capacity) {
this.capacity = capacity;
}

public synchronized void put(T element) throws InterruptedException {
while(queue.size() == capacity) {
wait();
}

queue.add(element);
notify(); // notifyAll() for multiple producer/consumer threads
}

public synchronized T take() throws InterruptedException {
while(queue.isEmpty()) {
wait();
}

T item = queue.remove();
notify(); // notifyAll() for multiple producer/consumer threads
return item;
}
}

There are a few things to note about the way in which you must use the wait and notify mechanisms.

Firstly, you need to ensure that any calls to wait() or notify() are within a synchronized region of code (with the wait() and notify() calls being synchronized on the same object). The reason for this (other than the standard thread safety concerns) is due to something known as a missed signal.

An example of this, is that a thread may call put() when the queue happens to be full, it then checks the condition, sees that the queue is full, however before it can block another thread is scheduled. This second thread then take()'s an element from the queue, and notifies the waiting threads that the queue is no longer full. Because the first thread has already checked the condition however, it will simply call wait() after being re-scheduled, even though it could make progress.

By synchronizing on a shared object, you can ensure that this problem does not occur, as the second thread's take() call will not be able to make progress until the first thread has actually blocked.

Secondly, you need to put the condition you are checking in a while loop, rather than an if statement, due to a problem known as spurious wake-ups. This is where a waiting thread can sometimes be re-activated without notify() being called. Putting this check in a while loop will ensure that if a spurious wake-up occurs, the condition will be re-checked, and the thread will call wait() again.


As some of the other answers have mentioned, Java 1.5 introduced a new concurrency library (in the java.util.concurrent package) which was designed to provide a higher level abstraction over the wait/notify mechanism. Using these new features, you could rewrite the original example like so:

public class BlockingQueue<T> {

private Queue<T> queue = new LinkedList<T>();
private int capacity;
private Lock lock = new ReentrantLock();
private Condition notFull = lock.newCondition();
private Condition notEmpty = lock.newCondition();

public BlockingQueue(int capacity) {
this.capacity = capacity;
}

public void put(T element) throws InterruptedException {
lock.lock();
try {
while(queue.size() == capacity) {
notFull.await();
}

queue.add(element);
notEmpty.signal();
} finally {
lock.unlock();
}
}

public T take() throws InterruptedException {
lock.lock();
try {
while(queue.isEmpty()) {
notEmpty.await();
}

T item = queue.remove();
notFull.signal();
return item;
} finally {
lock.unlock();
}
}
}

Of course if you actually need a blocking queue, then you should use an implementation of the BlockingQueue interface.

Also, for stuff like this I'd highly recommend Java Concurrency in Practice, as it covers everything you could want to know about concurrency related problems and solutions.

Notify and wait code scenario

Your synchronized blocks have "no" effect.

Your synchronized(this) just gets the lock for the Runnable instance where you also implement the run method.

Your Thread t1 will never be notified, it waits for the Runnable where you use the wait() method to get notified. The only object that holds a reference to this Runnable is the Thread Object t1 and that will (usually) not call notify() or notifyAll() on that Runnable.

I'm using an int[] for storing the int value as well as for holding the lock / monitor. The solution is only to show you how you could do it, not meant that this is good practice to do it this way.

I'd recommend to read a good tutorial about how synchronized in Java works.

I've modified you example so that it works as you expect.

public class NotifyAndWaitExample2 {
private static int[] i = {0};

public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (i) {
if (i[0] <= 0) {
System.out.println("i=" + i[0] + " in t1");
System.out.println(Thread.currentThread().getName() + " is running");
try {
i.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " is waken up");
}
}
});
Thread t4 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (i) {
i[0]++;
System.out.println("i=" + i[0] + "in t4");
System.out.println(Thread.currentThread().getName() + " is notifying");
try {
Thread.sleep(1000);
i.notifyAll();
System.out.println("notifying");
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
});
t1.start();
t4.start();
}
}

java wait notify methods using synchronized key word

The wait needs to happen in the 1st Runnable and you need to have access to an Object instance to wait on, so the t1 Thread instance won't work. In this code, I've created a separate lock object.

public static void main(String[] args) {
final Object lock = new Object();
new Thread(new Runnable() {
@Override
public void run() {
synchronized(lock) {
try {
lock.wait();
System.out.println("lock released");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
System.out.println("before sleep");
try {
Thread.sleep(1000);
System.out.println("before notify");
synchronized(lock) {
lock.notify();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}

Using thread notifications can be very hard to test. I'd recommend using a message based approach like Akka.

Implementing a simple turn-based game in Java using the wait-notify approach

If I have understood your code correctly, that submitWord method belongs to the Player class. The keyword synchronized should be used to obtain the monitor of a shared resource to limit different threads from accessing the same resource at the same time and avoid race conditions.

In your case, you're synchronizing over a Player thread which is not the right design. You should synchronize instead over the shared resource which is the game object in this scenario. Besides, try to use synchronized blocks rather than entire synchronized methods, as the latter are more likely to block.

Within the Player's run method you should check whether the thread can acquire the game resource first with a synchronized block, if they do, then you can check whether it's the Player's turn by confronting the turn index of the game object with the Player's index. If it's not the Player's turn it invokes the wait() method; otherwise it carries on with its task by invoking submitWord.

Here, I've tweaked your code. You forgot a notify (or notifyAll) call when you were returning false in your submitWord method. That might have caused some stuck scenarios when there were no combinations available.

//Now, this method can be called only under the condition the the game's monitor lock has been already acquired. So, it can only be invoked within a synchronized block.
private boolean submitWord() {
List<Tile> extracted = game.getBag().extractTiles(wordLength);
if(extracted.isEmpty()){
//notify is more efficient than notifyAll as it causes less overhead by awakening only one random thread instead of all the ones waiting
this.game.notify();

//you were returning without notifying here... This might have caused some stucking scenarios...
return false;
}

//game logic goes here - creating and validating the word

//Rotating the turn
game.turnIndex = (this.game.turnIndex + 1) % this.game.activePlayers.size();

game.turn = game.activePlayers.get(game.turnIndex);
this.game.notify();
return true;
}

@Override
public void run() {
do {
synchronized(this.game){
if (this.game.indexTurn == this.index){
this.running = this.submitWord();

//It's more efficient to check here whether the player must be removed or not as you already own the game's lock
if (!this.running){
this.game.activePlayers.remove(this);
}
} else {
try {
this.game.wait();
} catch(InterruptedException e) {
System.out.println("Something went wrong with " + this.name + "...");
e.printStackTrace();
}
}
}
} while(this.running);

//you should re-acquire the game's lock here since you're modifying the set of players
//synchronized(this.game){
// this.game.activePlayers.remove(this);
//}

if(this.game.winner == this){
System.out.println("Winner: " + this.name + " [" + this.score + " points]");
}
}

A simple wait() & notify() example - doesn't work

Both threads synchronize on the same object, this refers to the same object as remote and since both this synchronize blocks have infinite loops inside, this creates a problem. One of the thread will wait for the other one to finish, which never happens (because of the infinite loops).

To fix this, you should synchronize only the code that needs to be synchronized, like the wait() and notify() calls.



Related Topics



Leave a reply



Submit