последовательность ожидания и уведомления о потоках в Java - PullRequest
0 голосов
/ 09 ноября 2018

Я написал программу производителя / потребителя, как показано ниже.

 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, потому что, когда поток производителя имеет блокировку объекта, потребитель должен ждать блокировки, а когда потребитель снимает блокировку, производитель должен получить блокировку.

Любая помощь приветствуется.

Ответы [ 3 ]

0 голосов
/ 09 ноября 2018

Изучите, что этот учебник может вам помочь, он кажется очень похожим на вашу проблему, даже если он отличается от вашего примера только признаками того, что метод имеет ключевое слово synchronized.

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

0 голосов
/ 09 ноября 2018

Здесь:

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

Потребитель потребляет одну запись. Но в то же время очередь заблокирована , так что пока что ничего добавить нельзя.

Таким образом, производитель должен ждать, пока потребитель потребит, а затем потребитель должен ждать, пока производитель введет что-то новое.

Следовательно, следующее предположение

Только потребительский поток должен входить в режим ожидания только один раз.

неправильно.

0 голосов
/ 09 ноября 2018

Object.notify () разбудит поток, ожидающий блокировки, но не обязательно назначит ему приоритет для получения следующего, и Javadoc идентифицирует это поведение:

Пробужденный поток не сможет продолжить работу, пока текущий поток не снимет блокировку этого объекта. Пробужденный поток будет конкурировать обычным образом с любыми другими потоками, которые могут активно конкурировать за синхронизацию на этом объекте; например, пробужденный поток не имеет надежных привилегий или недостатков, поскольку он является следующим потоком, блокирующим этот объект .

Вероятно, происходит то, что поток, который только что снял блокировку, немедленно получает ее снова перед ожидаемым потоком. Если вы поместите спящий режим после уведомления (но , а не в синхронизированном блоке), вы, вероятно, увидите ожидаемый результат. В этом случае вы заставляете этот поток эффективно уступать другому потоку, который был уведомлен.

...