Как использовать ожидание и уведомление в Java без IllegalMonitorStateException?

У меня есть 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()". Я думаю, мне нужно использовать синхронизированную по-разному, но я не уверен, как это сделать.

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

Ответ 1

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

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

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

Ответ 2

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

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

Ответ 3

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

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

Если вам нужно вставить его, я бы создал потоки "n" для выполнения умножения ячеек (возможно, "n" - количество доступных вам ядер), а затем используйте ExecutorService и Future, чтобы одновременно отправлять множественные умножения.

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

Ответ 4

Скажем, у вас есть приложение "черного ящика" с некоторым классом с именем 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. Здесь вводится "Synchronize helper":

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();       
   }

}

Ответ 5

Вы можете только вызывать уведомление об объектах, где вы владеете своим монитором. Поэтому вам нужно что-то вроде

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

Ответ 6

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

Ответ 7

Я приведу простой пример, показывающий правильный способ использования wait и notify в Java. Поэтому я создам два класса с именем ThreadA и ThreadB. 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
            }
        }
    }

Ответ 8

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

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!");
}

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

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

Ответ 9

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

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

Ответ 10

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

Ответ 11

Это выглядит как ситуация для шаблона производителя-потребителя. Если вы используете java 5 или выше, вы можете рассмотреть возможность использования блокирующей очереди (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

Ответ 12

Вы правильно охраняли свой кодовый блок, когда вы вызываете метод wait(), используя synchronized(this).

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

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

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

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

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

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

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