如何在没有IllegalMonitorStateException的情况下在Java中使用等待和通知?


129

我有2个矩阵,我需要将它们相乘,然后打印每个单元格的结果。准备好一个单元格后,我就需要打印它,但是例如,即使[2] [0]的结果先准备好,我也需要在单元格[2] [0]之前打印[0] [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()”。我想我需要以不同的方式使用synced,但是我不确定如何使用。

如果有人可以帮助该代码正常工作,我将非常感谢。

Answers:


215

为了能够调用notify(),您需要在同一对象上进行同步。

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

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

29
while(!JobCompleted);选项通常不是一个好主意,因为它会以100%的速度将CPU占用,并不断检查同一变量(请参见此处
Matt Lyons

5
while(!JobCompleted) Thread.sleep(5); 没有这个问题
BeniBela

15
它仍然存在完全不同的问题。通常,轮询(反复检查是否满足某些条件,即您在做什么)通常比通知是否更改所述条件(即我在答案中概述的条件)要好得多。
庞贝

3
@huseyintugrulbuyukisik,wait只要当前线程在wait被调用的对象上具有锁定,便可以调用。使用synchronized块还是同步方法完全取决于您。
庞贝2014年

1
@BeniBela但是可以肯定肯定会变慢(关于侯赛因的原始问题)。
汤玛斯(Thomas)

64

在Java中使用waitand notifynotifyAll方法时,必须记住以下几点:

  1. 如果您期望多个线程正在等待锁,请使用notifyAll代替notify
  2. waitnotify方法必须在同步上下文中调用。有关更多详细说明,请参见链接。
  3. 始终wait()在循环中调用该方法,因为如果多个线程正在等待锁,并且其中一个线程获得了锁并重置了条件,则其他线程在唤醒后需要检查条件以查看是否需要再次等待或可以开始处理了。
  4. 使用相同的对象进行调用wait()notify()方法;每个对象都有其自己的锁,因此调用wait()对象A和notify()对象B毫无意义。

21

您是否需要穿线呢?我想知道您的矩阵有多大,以及打印一个线程而另一个执行乘法是否有益。

也许这一次值得在进行相对复杂的线程工作之前进行测量?

如果确实需要对其进行线程化,则可以创建“ n”个线程来执行单元的乘法(也许“ n”是您可以使用的核心数),然后使用ExecutorServiceFuture机制同时调度多个乘法。

这样,您可以根据内核数量优化工作,并且使用的是更高级别的Java线程工具(这会使工作变得更轻松)。将结果写回到接收矩阵,然后在完成所有“未来”任务后简单地打印此结果。


1
+1 @Greg我想您应该看一下Brian指出的java.util.concurrent包。
ATorras

1
+1还检查了),这本书也将教你正确的方式使用wait()和notify(jcip.net
CHII

14

假设您有一个名为“黑匣子”的应用程序,该类的某个类BlackBoxClass具有method 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();       
   }

}

7

您只能在拥有监视器的对象上调用通知。所以你需要像

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


3

我会正确的简单的例子告诉你正确的方式使用waitnotifyJava编写的。因此,我将创建两个名为ThreadAThreadB的类。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
            }
        }
    }

3

如果您想要如何交替执行线程,则使用简单:

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

当我有4个操作要以同步方式执行时,该如何工作?
saksham agarwal19年

2

我们可以调用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();
}

0

对于此特定问题,为什么不将各种结果存储在变量中,然后在处理完线程的最后部分后,可以按所需的任何格式打印。如果您要在其他项目中使用工作历史记录,则此功能特别有用。



0

wait()通过使用调用方法时,您已经正确地保护了代码块synchronized(this)

但是,当你调用没有采取相同的预防notify()方法不使用防护模块:synchronized(this)synchronized(someObject)

如果你指的是Oracle文档页面上的对象类,其中包含wait()notify()notifyAll()方法,你可以看到下面的预防措施在所有这三个方法

此方法只能由作为该对象的监视器的所有者的线程调用

在过去的7年中,许多事情已经改变,让我们synchronized在以下SE问题中寻找其他替代方法:

如果可以使用同步的(this),为什么还要使用ReentrantLock?

同步与锁定

避免在Java中同步(this)?

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.