如何在没有IllegalMonitorStateException的Java中使用wait和notify?

问题描述 投票:126回答:12

我有2个矩阵,我需要将它们相乘,然后打印每个单元格的结果。一旦一个单元格准备就绪,我需要打印它,但是例如我需要在单元格[2] [0]之前打印[0] [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)

multiplyThread中的第49行是“notify()”..我想我需要使用不同的同步,但我不知道如何。

如果有人可以帮助这些代码工作,我将非常感激。

java multithreading exception java-threads
12个回答
208
投票

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

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

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

0
投票

对于这个特殊问题,为什么不将各种结果存储在变量中,然后在处理最后一个线程时,您可以以任何您想要的格式打印。如果您要在其他项目中使用您的工作历史,这将特别有用。


0
投票

这看起来像生产者 - 消费者模式的情况。如果您正在使用java 5或更高版本,您可以考虑使用阻塞队列(java.util.concurrent.BlockingQueue)并将线程协调工作留给底层框架/ 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


0
投票

当您使用wait()调用synchronized(this)方法时,您已正确保护代码块。

但是当你在不使用防护块的情况下调用notify()方法时,你没有采取相同的预防措施:synchronized(this)synchronized(someObject)

如果您参考Object类的oracle文档页面,其中包含wait()notify()notifyAll()方法,您可以在以下三种方法中看到以下预防措施

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

在过去的7年中,许多事情都发生了变化,让我们在下面的SE问题中研究synchronized的其他替代方案:

Why use a ReentrantLock if one can use synchronized(this)?

Synchronization vs Lock

Avoid synchronized(this) in Java?


63
投票

在Java中使用waitnotifynotifyAll方法时,必须记住以下内容:

  1. 如果您希望多个线程等待锁定,请使用notifyAll而不是notify
  2. The wait and notify methods must be called in a synchronized context。请参阅链接以获取更详细的说明。
  3. 总是在循环中调用wait()方法,因为如果多个线程正在等待锁定并且其中一个线程获得锁定并重置条件,那么其他线程需要在它们唤醒后检查它们是否需要再次等待或者可以开始处理。
  4. 使用相同的对象来调用wait()notify()方法;每个对象都有自己的锁,因此在对象A上调用wait()并在对象B上调用notify()将没有任何意义。

20
投票

你需要解决这个问题吗?我想知道你的矩阵有多大,一个线程打印是否有任何好处,另一个是乘法。

也许在进行相对复杂的线程工作之前测量这段时间是否值得?

如果你确实需要线程化,我会创建'n'个线程来执行单元格的乘法运算(也许'n'是可用的核心数),然后使用ExecutorServiceFuture机制同时发送多个乘法。

这样您就可以根据内核数量优化工作,并且您正在使用更高级别的Java线程工具(这应该会让生活变得更轻松)。将结果写回接收矩阵,然后在完成所有Future任务后打印。


13
投票

假设你有一个名为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();       
   }

}

7
投票

您只能在拥有其监视器的对象上调用notify。所以你需要类似的东西

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

6
投票

notify()也需要同步


3
投票

我将以简单的例子向您展示在Java中使用waitnotify的正确方法。所以我将创建两个名为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
            }
        }
    }

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

2
投票

如果您想要如何执行线程,请简单使用: -

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
© www.soinside.com 2019 - 2024. All rights reserved.