簡體   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