Почему «уведомить» разбудить все ожидающие потоки, хотя только один поток должен быть затронут? - PullRequest
4 голосов
/ 11 марта 2020

В следующем коде два потребительских потока запускаются и становятся ожидающими. Поток продюсера запускается (очень вероятно) после этого и вызывает 'notify'. Все потоки используют производителя в качестве монитора.

Thread producer = new Thread() { 
    @Override
    public void run() {
        synchronized (this) {
            System.out.printf("notify at %d %n", getId());
            notify();
        }
    }
};

Runnable consumer = () -> {
    try {
        synchronized (producer) {
            long id = Thread.currentThread().getId();
            System.out.printf("wait at %d %n", id);
            producer.wait();
            System.out.printf("awakened: %d %n", id);
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
};

Stream.generate( () -> consumer )
        .limit(2)
        .map(Thread::new)
        .forEach(Thread::start);

Thread.sleep(3000); // consumer threads are (likely) waiting

producer.start();

От javado c для Object.notify :

Просыпается один ожидающий поток на мониторе этого объекта.

Код выдает такой (или аналогичный) вывод:

  wait at 13 
  wait at 14 
  notify at 12 
  awakened: 13 
  awakened: 14 

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

Скомпилировано и протестировано с помощью среды выполнения OpenJDK AdoptOpenJDK (сборка 11.0.5 + 10) в Windows 10, 64-бит.

Заранее спасибо!

1 Ответ

9 голосов
/ 11 марта 2020

Проблема заключается в том, что вместо произвольного Object вы используете Thread в качестве монитора.

Thread использует внутренние сигналы, как описано в Thread.join:

Когда поток завершается, вызывается метод this.notifyAll. Рекомендуется, чтобы приложения не использовали wait, notify или notifyAll на Thread экземплярах.

Общее предложение - всегда использовать выделенные объекты для wait / notify при этом никакой другой код не может получить доступ, чтобы избежать «ложных» уведомлений или ожиданий, подобных этому.

...