I wrote a producer/consumer program as below.
package com.myjava.concurrency.basics.waitnotify;
import java.util.PriorityQueue;
import java.util.Queue;
public class SharedObject {
private Queue<String> dataObject;
private final Object objLock = new Object();
public SharedObject() {
dataObject = new PriorityQueue<String>(1);
}
public void writeData(String data) {
synchronized (objLock) {
while (!dataObject.isEmpty()) {
System.out.println("Producer:Waiting");
try {
objLock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
dataObject.offer(data);
System.out.println(String.format("%s : %s",
Thread.currentThread().getName(), data));
objLock.notify();
}
}
public String readData() {
String result = null;
synchronized (objLock) {
while (dataObject.isEmpty()) {
System.out.println("Consumer:Waiting");
try {
objLock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
result = dataObject.poll();
System.out.println(String.format("%s : %s",
Thread.currentThread().getName(), result));
objLock.notify();
}
return result;
}
}
package com.myjava.concurrency.basics.waitnotify;
import java.util.Arrays;
import java.util.List;
public class TestWaitNotify {
public static void main(String[] args) {
SharedObject sharedObject = new SharedObject();
List<String> fruitsList = Arrays.asList("Apple", "Banana", "Orange");
int listSize = fruitsList.size();
Thread producer = new Thread(() -> {
System.out.println("producer thread started");
fruitsList.forEach(p -> {
sharedObject.writeData(p);
});
}, "producer");
Thread consumer = new Thread(() -> {
System.out.println("consumer thread started");
for (int i = 0; i < listSize; i++) {
sharedObject.readData();
}
}, "consumer");
consumer.start();
producer.start();
}
}
I got the output, as below:
producer thread started
consumer thread started
Consumer:Waiting
producer : Apple
Producer:Waiting
consumer : Apple
Consumer:Waiting
producer : Banana
Producer:Waiting
consumer : Banana
Consumer:Waiting
producer : Orange
consumer : Orange
Here is my question:
I expected the below sequence, with this program:
producer thread started
consumer thread started
Consumer:Waiting // assuming consumer thread begins first
producer : Apple
consumer : Apple
producer : Banana
consumer : Banana
producer : Orange
consumer : Orange
Only consumer thread should enter in wait mode only once. After the first notify, the threads should not enter while loop because when producer thread has the object lock, consumer should wait for the lock and when consumer releases the lock the producer should acquire the lock.
Any help is appreciated.
Object.notify() will wake up a thread waiting on the lock but it doesn't necessarily prioritize it to acquire next and the javadoc identifies this behavior:
The awakened thread will not be able to proceed until the current thread relinquishes the lock on this object. The awakened thread will compete in the usual manner with any other threads that might be actively competing to synchronize on this object; for example, the awakened thread enjoys no reliable privilege or disadvantage in being the next thread to lock this object.
Likely what is happening is the thread that just relinquished the lock is immediately acquiring it again in front of the thread you expect. If you put a sleep after the notify (but not in the synchronized block) you're likely to see the output you were expecting. In this cause you're forcing that thread to effectively yield to the other thread that has been notified.