繁体   English   中英

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

[英]How to use wait and notify in Java without IllegalMonitorStateException?

我有 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)

multiplyThread中的第 49 行是“notify()”……我想我需要以不同的方式使用同步,但我不确定如何使用。

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

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

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

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

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

  1. 如果您预计会有多个线程等待锁定,请使用notifyAll而不是notify
  2. 必须在同步上下文中调用waitnotify方法 有关更详细的说明,请参阅链接。
  3. 总是在循环中调用wait()方法,因为如果多个线程正在等待一个锁并且其中一个线程获得了锁并重置了条件,那么其他线程需要在它们醒来后检查条件以查看它们是否需要再次等待或可以开始处理。
  4. 使用相同的对象来调用wait()notify()方法; 每个对象都有自己的锁,因此在对象 A 上调用wait()和在对象 B 上调用notify()没有任何意义。

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

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

如果您确实需要线程化,我将创建“n”个线程来执行单元格的乘法(也许“n”是您可用的内核数),然后使用ExecutorServiceFuture机制同时调度多个乘法.

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

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

}

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

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

notify()需要同步

我将通过简单的示例向您展示在 Java 中使用waitnotify的正确方法。 所以我将创建两个名为ThreadAThreadB 的类。 线程A 将调用线程B。

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
            }
        }
    }

简单使用,如果你想如何交替执行线程:-

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

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

通过在同一个类的另一个对象上调用通知来恢复这个

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

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

这看起来像是生产者-消费者模式的一种情况。 如果您使用的是 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

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

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

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

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

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

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

同步与锁定

在 Java 中避免同步(这个)?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM