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

Posted

技术标签:

【中文标题】如何在没有 IllegalMonitorStateException 的情况下在 Java 中使用等待和通知?【英文标题】:How to use wait and notify in Java without IllegalMonitorStateException? 【发布时间】:2010-10-27 13:38:10 【问题描述】:

我有 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()”。我想我需要以不同的方式使用同步,但我不确定如何。

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

【问题讨论】:

【参考方案1】:

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

synchronized (someObject) 
    someObject.wait();


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

【讨论】:

while(!JobCompleted); 选项通常不是一个好主意,因为它会占用你的 CPU 以 100% 不断检查同一个变量(请参阅 here) while(!JobCompleted) Thread.sleep(5); 没有这个问题 它仍然存在完全不同的问题。轮询(反复检查是否满足某些条件,即您在做什么)通常不如在所述条件发生更改时收到通知(即我在答案中概述的内容)。 @huseyintugrulbuyukisik 只要当前线程锁定了调用wait 的对象,您就可以调用wait。是使用 synchronized 块还是同步方法完全取决于您。 @BeniBela 但它肯定会更慢(关于 Huseyin 的原始问题)。【参考方案2】:

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

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

【讨论】:

【参考方案3】:

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

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

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

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

【讨论】:

+1 @Greg 我认为你应该看看 java.util.concurrent 包,正如 Brian 指出的那样。 +1 并查看这本书,它还将教你正确使用 wait() 和 notify() jcip.net【参考方案4】:

假设您有一个“黑匣子”应用程序,其中包含一个名为 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 电话之前继续您的代码。这里输入“同步助手”:

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


【讨论】:

【参考方案5】:

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

synchronized(threadObject)

   threadObject.notify();

【讨论】:

【参考方案6】:

notify() 也需要同步

【讨论】:

【参考方案7】:

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

【讨论】:

【参考方案8】:

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

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 个操作要以同步方式执行时,它是如何工作的?【参考方案9】:

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

【讨论】:

【参考方案10】:

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

【讨论】:

【参考方案11】:

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

【讨论】:

【参考方案12】:

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

但是当你调用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?

【讨论】:

以上是关于如何在没有 IllegalMonitorStateException 的情况下在 Java 中使用等待和通知?的主要内容,如果未能解决你的问题,请参考以下文章

我如何在没有订单的情况下填充托盘架存储?

如何在没有没有索引的 url 的情况下调用 codeigniter 控制器功能

如何在没有 UITableViewController 的情况下结束 UIRefreshControl

如何在没有 GPU 的情况下运行 tensorflow?

如何在没有'id'列的MySql中获取最后插入的记录?

如何在没有 iMessage 的情况下发送短信