简体   繁体   English

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

[英]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.第一次通知后,线程不应该进入while循环,因为当生产者线程有对象锁时,消费者应该等待锁,当消费者释放锁时,生产者应该获取锁。

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: 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 .例如,被唤醒的线程在成为下一个锁定该对象的线程方面没有可靠的特权或劣势

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.在这个原因中,您强制该线程有效地让位于已通知的另一个线程。

Examine this tutorial may could help you, it seem's very similiar to your problem even it only differences from your example is method signs they have synchronized keyword by the way.检查本教程可能会对您有所帮助,它似乎与您的问题非常相似,即使它与您的示例的唯一区别是方法标志他们顺便说一下synchronized关键字。

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

Here:这里:

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

The consumer consumes one entry.消费者消费一个条目。 But at the same time, the queue is locked , so nothing can be added in the meantime.但与此同时,队列被锁定,因此在此期间不能添加任何内容。

So the producer has to wait for the consumer to consume, then the consumer has to wait for the producer to put something new in.所以生产者必须等待消费者消费,然后消费者必须等待生产者放入新的东西。

Therefore the following assumption因此下面的假设

Only consumer thread should enter in wait mode only once.只有消费者线程应该只进入等待模式一次。

is wrong.是错的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM