【问题标题】:sequence of wait and notify of threads in javajava中线程的等待和通知序列
【发布时间】:2018-11-09 04:25:52
【问题描述】:

我写了一个生产者/消费者程序如下。

 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

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

感谢任何帮助。

【问题讨论】:

    标签: java multithreading concurrency blocking


    【解决方案1】:

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

    在当前线程放弃对该对象的锁定之前,被唤醒的线程将无法继续。 被唤醒的线程将以通常的方式与任何其他可能正在积极竞争以同步此对象的线程进行竞争;例如,被唤醒的线程在成为下一个锁定该对象的线程方面没有任何可靠的特权或劣势

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

    【讨论】:

    • "刚刚放弃锁的线程正在立即再次获取它",这是我能看到的唯一可能性。但是,我的理解是,当消费者释放锁时,生产者应该先于消费者线程获取锁,因为与消费者线程相比,生产者等待的时间很长。谢谢@约翰
    • 我在代码的每个同步块的开头和结尾添加了打印语句,并注意到正在释放锁的线程(例如生产者)是立即获取的。只有当当前线程(即生产者)进入等待状态时,等待中的线程(在这种情况下为消费者)才获取锁。所以,@John 的解释是正确且详细的。
    【解决方案2】:

    检查本教程可能会对您有所帮助,它似乎与您的问题非常相似,即使它与您的示例的唯一区别是它们具有 synchronized 关键字的方法标志。

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

    【讨论】:

    • 在该程序的 while 循环中添加 print 语句后,我得到了与我的匹配的输出。消费者将在完成迭代后等待模式,这就是我感到困惑的一点。生产者#1 put: 0 Consumer #1 got: 0 Consumer:Waiting Producer #1 put: 1 Consumer #1 got: 1 Consumer:Waiting 所以,这没有帮助。但是,我很想知道原因,但不知道相似/相同的输出。
    【解决方案3】:

    这里:

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

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

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

    因此有以下假设

    只有消费者线程只能进入等待模式一次。

    错了。

    【讨论】:

    • "消费者消费了一个条目,但同时队列被锁定,此时不能添加任何内容。"我同意直到这一点。但是看程序,consumer消费完数据后,调用notify()。此时生产者线程的第二次迭代正在等待在同步语句中获取锁。正确的?并且它不在while循环内,因为此时锁仍在消费者手中。当消费者线程完成 notify() 后释放锁,进入下一次迭代。
    • 继续上述评论。当生产者线程在第二次迭代中获得锁时, dataObject.isEmpty() 应该是肯定的,因此它不应该进入 while 循环。基于这个假设,我预计唯一的“消费者”应该只进入一次 while 循环,之后它只是生产者/消费者之间的锁释放/获取游戏。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-20
    • 1970-01-01
    • 2020-08-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多