如何在 Java 中使用等待和通知而不出现 IllegalMonitor 异常?

我有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行是「通知()」。.我认为我需要使用同步不同,但我不知道如何。

如果有人能够帮助这个代码工作,我将非常感激。

238600 次浏览

notify()也需要同步

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

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


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

您只能在拥有其监视器的对象上调用通知

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

你需要把这个穿起来吗?我想知道你的矩阵有多大以及一个线程打印而另一个进行乘法是否有好处。

也许在进行相对复杂的线程处理工作之前,这段时间值得测量一下?

如果您确实需要执行线程,我将创建’n’线程来执行单元格的乘法运算(也许’n’是可用的核心数量) ,然后使用 执行服务未来机制来同时分派多个乘法运算。

这样,您就可以根据核心的数量来优化工作,并且您正在使用更高级别的 Java 线程工具(这应该会使工作变得更容易)。将结果写回到接收矩阵中,然后在未来的所有任务完成后简单地打印出来。

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

  1. 如果期望有多个线程等待锁,则使用 notifyAll而不是 notify
  2. waitnotify方法必须在同步上下文 中调用。
  3. 总是在循环中调用 wait()方法,因为如果多个线程正在等待一个锁,其中一个获得了锁并重置了条件,那么其他线程需要在唤醒后检查条件,以确定是否需要再次等待或可以开始处理。
  4. 使用相同的对象调用 wait()notify()方法; 每个对象都有自己的锁,因此在对象 A 上调用 wait()和在对象 B 上调用 notify()将没有任何意义。

假设您有一个“黑盒”应用程序,其中有一个名为 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();
}


}

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

这看起来像是生产者-消费者模式的情况。如果您使用的是 java 5或更高版本,可以考虑使用阻塞队列(java.util.current)。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

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

我们可以调用 tification 来恢复等待对象的执行,如下所示

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

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

但是,在不使用保护块 synchronized(this)synchronized(someObject)调用 notify()方法时,您没有采取同样的预防措施

如果您参考关于 反对类的 Oracle 文档页面,其中包含 wait()notify()notifyAll()方法,您可以在所有这三个方法中看到以下预防措施

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

在过去的7年里,很多事情都发生了变化,让我们在下面的 SE 问题中看看 synchronized的其他替代品:

如果可以使用 synized (this) ,为什么要使用 ReentrantLock?

同步与锁定

在 Java 中避免同步?

如果您想要如何执行线程,可以选择简单使用:-

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