Search code examples
javamultithreadingconcurrencyblocking

sequence of wait and notify of threads in java


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.


Solution

  • 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.