Использование notify() и notifyAll() не работает в коде
Я вставил код ниже. Это достаточно прокомментировано. Ясно о ожидании (). Когда приходит сюда, он прыгает в другой блок. Эта часть я дубовая. Я сомневаюсь, почему мы используем notify и notifyAll(). Если вы удалите эти два кода из приведенного ниже, он работает нормально.
class Reader extends Thread{
Calculator c;
//here we didn't write no-arg constructor. Note this.
// one - arg constructor.
public Reader(Calculator calc){
c = calc;
}
public void run(){
synchronized(c){
// 2. Acquiring the object lock and executes this code of block.
try{
System.out.println("Waiting for calculation...");
c.wait();
// 3. Release the object lock and moves to the second synchronize block below
// 6. Later the object get the lock here and moves on.
}catch(InterruptedException e){
}
System.out.println("Total is: "+c.total);
}
}
public static void main(String[] args){
//Instantiating new with no-arg. One more doubt, How this work without no-arg constructor above. Please explain.
Calculator calculator = new Calculator();
//Instantiating new with one-arg
new Reader(calculator).start();
new Reader(calculator).start();
new Reader(calculator).start();
// 1. Once you start here it will goto first synchronized code block above
calculator.start();
}
}
class Calculator extends Thread{
int total;
public void run(){
synchronized(this){
// 4. This block acquires that object lock and executes the code block below.
for(int i=0;i<100;i++){
total +=i;
}
// 5. As per documentation, If we give notify() it will release the object lock to only one thread object of its choice.
// If we use notifyAll(); it will release the object lock to all the three thread object.
notify();
// My doubt here is without using the notify or notifyAll it is working fine.
// As per docs if we use notify() only one object should get the lock. That is also not working here.
}
}
}
3 ответа
Общий комментарий: JavadocObject#wait
говорится, что
Как и в версии с одним аргументом, возможны прерывания и ложные пробуждения, и этот метод всегда должен использоваться в цикле.
Таким образом, ожидающий поток может проснуться без уведомления, и ваш проект должен принять это во внимание, ожидая в цикле и проверяя условие выхода (см. Пример в javadoc).
В вашем случае, однако, проблема немного отличается. Согласно Thread#join
Javadoc:
Когда поток завершает работу, вызывается метод this.notifyAll. Рекомендуется, чтобы приложения не использовали wait, notify или notifyAll для экземпляров Thread.
Поэтому, когда ваш калькулятор заканчивается, он вызывает this.notifyAll()
и просыпается все ожидающие темы.
Как это исправить?
Вы должны использовать отдельный объект блокировки, похожий на: private final Object lock = new Object();
в своем калькуляторе и обеспечьте читателя для получения.
Нет никакой гарантии относительно порядка, в котором потоки начинают работать. Если калькулятор запускается первым, то его notify
будет потеряно, и ни один Читатель не будет уведомлен.
Вот исправленная версия вышеупомянутой программы, которая имеет некоторый смысл notify() и notifyAll(). Здесь я реализовал Runnable вместо расширения Threads. Это единственное изменение, которое я сделал. Работает отлично.
class Reader implements Runnable{
Calculator c;
public Reader(Calculator calc){
c = calc;
}
public void run(){
synchronized(c){
try{
System.out.println("Waiting for calculation...");
c.wait();
}catch(InterruptedException e){
}
System.out.println("Total is: "+c.total);
}
}
public static void main(String[] args){
Calculator calculator = new Calculator();
Reader read = new Reader(calculator);
Thread thr = new Thread(read);
Thread thr1 = new Thread(read);
Thread thr2 = new Thread(read);
thr.start();
thr1.start();
thr2.start();
new Thread(calculator).start();
}
}
class Calculator implements Runnable{
int total;
public void run(){
System.out.println("Entered Calculator");
synchronized(this){
for(int i=0;i<20;i++){
total +=i;
}
notifyAll();
}
}
}