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

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

12 ответов


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

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() на объекте A и notify() на объекте B не будет никакого смысла.

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

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

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

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


предположим, у вас есть приложение "черный ящик" с некоторым классом с именем 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();       
   }

}

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

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

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


Я прямо простой пример показать вам правильный способ использования 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
            }
        }
    }

мы можем вызвать 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 или выше, вы можете рассмотреть возможность использования очереди блокировки (java.утиль.параллельный.BlockingQueue) и оставить работу по координации потоков базовой реализации framework/api. См. пример из Ява 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 (this)?

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

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