Как использовать wait и notify в Java без исключения IllegalMonitorStateException?

StackOverflow https://stackoverflow.com/questions/886722

Вопрос

У меня есть 2 матрицы, и мне нужно их умножить, а затем распечатать результаты для каждой ячейки.Как только одна ячейка будет готова, мне нужно распечатать ее, но, например, мне нужно напечатать ячейку [0][0] перед ячейкой [2][0], даже если результат [2][0] готов первым.Поэтому мне нужно распечатать его по заказу.Итак, моя идея состоит в том, чтобы заставить поток принтера ждать, пока multiplyThread уведомляет его о том, что правильная ячейка готова к печати, а затем printerThread распечатает ячейку и вернется к ожиданию и так далее..

Итак, у меня есть этот поток, который выполняет умножение:

public void run() 
{
    int countNumOfActions = 0; // How many multiplications have we done
    int maxActions = randomize(); // Maximum number of actions allowed

    for (int i = 0; i < size; i++)
    {       
        result[rowNum][colNum] = result[rowNum][colNum] + row[i] * col[i];
        countNumOfActions++;
        // Reached the number of allowed actions
        if (countNumOfActions >= maxActions)
        {
            countNumOfActions = 0;
            maxActions = randomize();
            yield();
        }   
    }
    isFinished[rowNum][colNum] = true;
    notify();
}

Поток, который печатает результат для каждой ячейки:

public void run()
{
    int j = 0; // Columns counter
    int i = 0; // Rows counter
    System.out.println("The result matrix of the multiplication is:");

    while (i < creator.getmThreads().length)
    {
        synchronized (this)
        {
            try 
            {
                this.wait();
            } 
            catch (InterruptedException e1) 
            {
            }
        }
        if (creator.getmThreads()[i][j].getIsFinished()[i][j] == true)
        {
            if (j < creator.getmThreads()[i].length)
            {
                System.out.print(creator.getResult()[i][j] + " ");
                j++;
            }
            else
            {
                System.out.println();
                j = 0;
                i++;
                System.out.print(creator.getResult()[i][j] + " ");
            }
        }
    }

Теперь он выдает мне эти исключения:

Exception in thread "Thread-9" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-6" Exception in thread "Thread-4" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-5" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-8" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-7" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-11" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-10" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-12" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)

строка 49 в multiplyThread это "notify()" .. Я думаю, мне нужно использовать synchronized по-другому, но я не уверен, как.

Если кто-нибудь может помочь этому коду работать, я буду очень признателен.

Это было полезно?

Решение

Чтобы иметь возможность позвонить уведомить() вам нужно выполнить синхронизацию на одном и том же объекте.

synchronized (someObject) {
    someObject.wait();
}

/* different thread / object */
synchronized (someObject) {
    someObject.notify();
}

Другие советы

При использовании wait и notify или notifyAll методы в Java необходимо помнить следующие вещи:

  1. Использование notifyAll вместо того , чтобы notify если вы ожидаете, что более одного потока будут ожидать блокировки.
  2. В wait и notify методы должны вызываться в синхронизированном контексте.Смотрите ссылку для более подробного объяснения.
  3. Всегда вызывайте wait() метод в цикле, потому что если несколько потоков ожидают блокировки, и один из них получил блокировку и сбросил условие, то другим потокам необходимо проверить условие после пробуждения, чтобы увидеть, нужно ли им снова ждать или они могут начать обработку.
  4. Используйте один и тот же объект для вызова wait() и notify() способ;каждый объект имеет свою собственную блокировку, поэтому вызов wait() на объекте А и notify() на объекте B не будет иметь никакого смысла.

Вам вообще нужно это делать ?Мне интересно, насколько велики ваши матрицы, и есть ли какая-либо выгода в том, что один поток печатает, в то время как другой выполняет умножение.

Возможно , стоило бы измерить это время , прежде чем выполнять относительно сложную потоковую работу ?

Если вам действительно нужно выполнить поток, я бы создал 'n' потоков для выполнения умножения ячеек (возможно, 'n' - это количество доступных вам ядер), а затем использовал Служба исполнителя и Будущее механизм для одновременной отправки нескольких умножений.

Таким образом, вы можете оптимизировать работу в зависимости от количества ядер, и вы используете инструменты потоковой обработки Java более высокого уровня (что должно упростить жизнь).Запишите результаты обратно в принимающую матрицу, а затем просто распечатайте ее, как только все ваши будущие задачи будут выполнены.

Допустим, у вас есть приложение "черный ящик" с некоторым классом по имени BlackBoxClass у этого есть метод doSomething();.

Кроме того, у вас есть наблюдатель или слушатель с именем onResponse(String resp) это будет вызвано BlackBoxClass спустя неизвестное время.

Поток прост:

private String mResponse = null; 
 ...
BlackBoxClass bbc = new BlackBoxClass();
   bbc.doSomething();
...
@override
public void onResponse(String resp){        
      mResponse = resp;       
}

Допустим, мы не знаем, что происходит с BlackBoxClass и когда мы должны получить ответ, но вы не хотите продолжать свой код, пока не получите ответ или, другими словами, получите onResponse звони.Здесь вводится "Помощник по синхронизации":

public class SyncronizeObj {
public void doWait(long l){
    synchronized(this){
        try {
            this.wait(l);
        } catch(InterruptedException e) {
        }
    }
}

public void doNotify() {
    synchronized(this) {
        this.notify();
    }
}

public void doWait() {
    synchronized(this){
        try {
            this.wait();
        } catch(InterruptedException e) {
        }
    }
}
}

Теперь мы можем реализовать то, что хотим:

public class Demo {

private String mResponse = null; 
 ...
SyncronizeObj sync = new SyncronizeObj();

public void impl(){

BlackBoxClass bbc = new BlackBoxClass();
   bbc.doSomething();

   if(mResponse == null){
      sync.doWait();
    }

/** at this momoent you sure that you got response from  BlackBoxClass because
  onResponse method released your 'wait'. In other cases if you don't want wait too      
  long (for example wait data from socket) you can use doWait(time) 
*/ 
...

}


@override
public void onResponse(String resp){        
      mResponse = resp;
      sync.doNotify();       
   }

}

Вы можете вызывать notify только для объектов, монитором которых вы владеете.Итак, вам нужно что-то вроде

synchronized(threadObject)
{
   threadObject.notify();
}

notify() также необходимо синхронизировать

Я на простом примере покажу вам, как правильно использовать wait и notify на языке Java.Итак, я создам два класса с именами ТредадА & Изношенный.ThreadA вызовет ThreadB.

public class ThreadA {
    public static void main(String[] args){
        ThreadB b = new ThreadB();//<----Create Instance for seconde class
        b.start();//<--------------------Launch thread

        synchronized(b){
            try{
                System.out.println("Waiting for b to complete...");
                b.wait();//<-------------WAIT until the finish thread for class B finish
            }catch(InterruptedException e){
                e.printStackTrace();
            }

            System.out.println("Total is: " + b.total);
        }
    }
} 

и для класса ThreadB:

class ThreadB extends Thread{
    int total;
    @Override
    public void run(){
        synchronized(this){
            for(int i=0; i<100 ; i++){
                total += i;
            }
            notify();//<----------------Notify the class wich wait until my    finish 
//and tell that I'm finish
            }
        }
    }

мы можем вызвать notify, чтобы возобновить выполнение ожидающих объектов следующим образом

public synchronized void guardedJoy() {
    // This guard only loops once for each special event, which may not
    // be the event we're waiting for.
    while(!joy) {
        try {
            wait();
        } catch (InterruptedException e) {}
    }
    System.out.println("Joy and efficiency have been achieved!");
}

возобновите это, вызвав notify для другого объекта того же класса

public synchronized notifyJoy() {
    joy = true;
    notifyAll();
}

Простое использование, если вы хотите, как альтернативно выполнять потоки :-

public class MyThread {
    public static void main(String[] args) {
        final Object lock = new Object();
        new Thread(() -> {
            try {
                synchronized (lock) {
                    for (int i = 0; i <= 5; i++) {
                        System.out.println(Thread.currentThread().getName() + ":" + "A");
                        lock.notify();
                        lock.wait();
                    }
                }
            } catch (Exception e) {}
        }, "T1").start();

        new Thread(() -> {
            try {
                synchronized (lock) {
                    for (int i = 0; i <= 5; i++) {
                        System.out.println(Thread.currentThread().getName() + ":" + "B");
                        lock.notify();
                        lock.wait();
                    }
                }
            } catch (Exception e) {}
        }, "T2").start();
    }
}

ответ :-

T1:A
T2:B
T1:A
T2:B
T1:A
T2:B
T1:A
T2:B
T1:A
T2:B
T1:A
T2:B

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

Это похоже на ситуацию по схеме производитель-потребитель.Если вы используете java 5 или выше, вы можете рассмотреть возможность использования blocking queue(java.util.concurrent.BlockingQueue) и оставить работу по координации потоков базовой реализации framework / api.Смотрите пример из java 5:http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/BlockingQueue.html или java 7 (тот же пример):http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/BlockingQueue.html

Вы должным образом защитили свой блок кода при вызове wait() метод с использованием synchronized(this).

Но вы не приняли таких же мер предосторожности, когда звонили notify() способ без использования защищенного блока : synchronized(this) или synchronized(someObject)

Если вы обратитесь к странице документации oracle на Объект класс, который содержит wait() ,notify(), notifyAll() методы, вы можете увидеть ниже меры предосторожности во всех этих трех методах

Этот метод должен вызываться только потоком, который является владельцем монитора этого объекта

За последние 7 лет многое изменилось, и давайте рассмотрим другие альтернативы synchronized в приведенных ниже вопросах SE:

Зачем использовать ReentrantLock, если можно использовать synchronized (это)?

Синхронизация против блокировки

Избегать синхронизации (этого) в Java?

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top