У меня есть один поток калькулятора, который вычисляет сумму числа от 1 до 50 и несколько потоков чтения, которые показывают результат после того, как поток калькулятора готов. У меня есть возможность вызвать notify() и notifyAll(), чтобы сигнализировать потокам чтения, что результат вычисления готов к отображению. В классе LINE B класса Calculator, если я вызываю метод notifyAll(), результат печатается 4 раза, как ожидалось. Но когда я заменяю LINE B только уведомлением(), я вижу, что результат напечатан 4 раза, что кажется неправильным. Я понимаю, что notify() будет только просыпать один из потоков, который ждет, а не все. Почему все потоки просыпаются и печатают результат, когда я вызываю уведомление?
public class ThreadWaitNotify {
public static void main(String[] args) {
Calculator c = new Calculator();
Reader r = new Reader(c);
Reader r2 = new Reader(c);
Reader r3 = new Reader(c);
Reader r4 = new Reader(c);
r.start();
r2.start();
r3.start();
r4.start();
try {
Thread.sleep(500L);
} catch (InterruptedException e) {
e.printStackTrace();
}
c.start();
}
}
Класс чтения:
class Reader extends Thread {
Calculator c;
public Reader(Calculator c) {
this.c = c;
}
@Override
public void run() {
synchronized (c) {
try {
System.out.println(Thread.currentThread().getName() + " Waiting for calculations: ");
c.wait(); // LINE A
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " Total: " + c.getSum());
}
}
}
Класс калькулятора:
class Calculator extends Thread {
private int sum = 0;
@Override
public void run() {
synchronized (this) {
for (int i = 1; i <= 50; i++) {
sum += i;
}
notify(); // LINE B
}
}
public int getSum() {
return sum;
}
}
Вывод:
Thread-1 Waiting for calculations:
Thread-4 Waiting for calculations:
Thread-3 Waiting for calculations:
Thread-2 Waiting for calculations:
Thread-1 Total: 1275
Thread-2 Total: 1275
Thread-3 Total: 1275
Thread-4 Total: 1275
======================
UPDATE: Использование объекта в качестве монитора/блокировки вместо экземпляра Thread приводит к правильному поведению.
Обновлен класс ThreadWaitNotify:
public class ThreadWaitNotify {
public static void main(String[] args) {
Object monitor = new Object();
Calculator c = new Calculator(monitor);
Reader r = new Reader(c, monitor);
Reader r2 = new Reader(c, monitor);
Reader r3 = new Reader(c, monitor);
Reader r4 = new Reader(c, monitor);
r.start();
r2.start();
r3.start();
r4.start();
try {
Thread.sleep(500L);
} catch (InterruptedException e) {
e.printStackTrace();
}
c.start();
}
}
Обновленный класс чтения:
class Reader extends Thread {
Calculator c;
Object monitor;
public Reader(Calculator c, Object monitor) {
this.c = c;
this.monitor = monitor;
}
@Override
public void run() {
synchronized (monitor) {
try {
System.out.println(Thread.currentThread().getName() + " Waiting for calculations: ");
monitor.wait(); // LINE A
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " Total: " + c.getSum());
}
}
}
Обновлен класс калькулятора:
class Calculator extends Thread {
private int sum = 0;
Object monitor;
public Calculator(Object monitor) {
this.monitor = monitor;
}
@Override
public void run() {
synchronized (monitor) {
for (int i = 1; i <= 50; i++) {
sum += i;
}
monitor.notify(); // LINE B
}
}
public int getSum() {
return sum;
}
}