java 中线程的等待和通知顺序

sequence of wait and notify of threads in java

我写了一个producer/consumer程序如下。

 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();

}
}

我得到了如下输出:

 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

这是我的问题:

我希望这个程序有以下顺序:

     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

只有消费者线程只能进入等待模式一次。第一次通知后,线程不要进入while循环,因为当生产者线程有对象锁时,消费者应该等待锁,当消费者释放锁时,生产者应该获得锁。

感谢任何帮助。

Object.notify() 将唤醒等待锁的线程,但它不一定优先获取下一个锁,javadoc 标识了此行为:

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.

可能发生的情况是刚刚放弃锁的线程立即在您期望的线程前面再次获取锁。如果您在通知后休眠(但 而不是 在同步块中),您可能会看到您期望的输出。在这种情况下,您强制该线程有效地屈服于已通知的其他线程。

检查本教程可能会对您有所帮助,它看起来与您的问题非常相似,即使它与您的示例的唯一区别是方法符号,顺便说一句,它们有 synchronized 关键字。

https://www.tutorialspoint.com/javaexamples/thread_procon.htm

这里:

while (dataObject.isEmpty()) {
  System.out.println("Consumer:Waiting");

消费者消费一个条目。但与此同时,队列已锁定,因此在此期间无法添加任何内容。

所以生产者必须等待消费者消费,然后消费者必须等待生产者放入新的东西。

因此有以下假设

Only consumer thread should enter in wait mode only once.

错了。