Использование 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();
        }
    }
}
Другие вопросы по тегам