Bagaimana cara menggunakan wait and notify di Java tanpa IllegalMonitorStateException?


129

Saya memiliki 2 matriks dan saya perlu melipatgandakannya dan kemudian mencetak hasil dari setiap sel. Segera setelah satu sel siap saya perlu mencetaknya, tetapi misalnya saya perlu mencetak sel [0] [0] sebelum sel [2] [0] bahkan jika hasil [2] [0] siap terlebih dahulu . Jadi saya perlu mencetaknya berdasarkan pesanan. Jadi ide saya adalah membuat utas printer menunggu sampai multiplyThreadmemberitahukan bahwa sel yang tepat siap untuk dicetak dan kemudian printerThreadakan mencetak sel dan kembali menunggu dan seterusnya ..

Jadi saya punya utas ini yang melakukan multiplikasi:

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

Utas yang mencetak hasil dari setiap sel:

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] + " ");
            }
        }
    }

Sekarang ini melempar saya pengecualian ini:

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)

baris 49 in multiplyThreadadalah "notify ()" .. Saya pikir saya perlu menggunakan sinkronisasi tetapi saya tidak yakin bagaimana caranya.

Jika ada yang bisa membantu kode ini bekerja, saya akan sangat menghargainya.

Jawaban:


215

Agar dapat memanggil notifikasi () Anda harus melakukan sinkronisasi pada objek yang sama.

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

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

29
The while(!JobCompleted);pilihan adalah biasanya ide yang buruk karena mengikat CPU Anda di 100% memeriksa variabel yang sama terus-menerus (lihat di sini )
Matt Lyons

5
while(!JobCompleted) Thread.sleep(5); tidak memiliki masalah itu
BeniBela

15
Itu masih memiliki masalah menjadi sesuatu yang sama sekali berbeda. Polling (berulang kali memeriksa jika beberapa kondisi terpenuhi, yaitu apa yang Anda lakukan) umumnya kurang disukai daripada diberitahu jika kondisi tersebut diubah (yaitu apa yang saya uraikan dalam jawaban).
Bombe

3
@huseyintugrulbuyukisik Anda dapat memanggil waitsetiap kali utas saat ini memiliki kunci pada objek yang waitdisebut. Apakah Anda menggunakan synchronizedblok atau metode yang disinkronkan sepenuhnya terserah Anda.
Bombe

1
@BeniBela Tapi itu bisa diharapkan lebih lambat pasti (tentang pertanyaan asli Huseyin).
Thomas

64

Saat menggunakan metode waitdan notifyatau notifyAlldi Jawa hal-hal berikut harus diingat:

  1. Gunakan notifyAllalih-alih notifyjika Anda mengharapkan lebih dari satu utas akan menunggu kunci.
  2. Itu wait dan notifyharus dipanggil dalam konteks yang disinkronkan . Lihat tautan untuk penjelasan yang lebih terperinci.
  3. Selalu panggil wait()metode ini dalam satu lingkaran karena jika beberapa utas sedang menunggu kunci dan salah satu dari mereka mendapatkan kunci dan mengatur ulang kondisinya, maka utas lainnya perlu memeriksa kondisi setelah mereka bangun untuk melihat apakah mereka perlu menunggu lagi atau dapat mulai memproses.
  4. Gunakan objek yang sama untuk panggilan wait()dan notify()metode; setiap objek memiliki kunci sendiri sehingga memanggil wait()objek A dan notify()objek B tidak masuk akal.

21

Apakah Anda perlu utas ini sama sekali? Saya bertanya-tanya seberapa besar matriks Anda, dan apakah ada untungnya memiliki satu cetakan ulir sementara yang lain melakukan penggandaan.

Mungkin ada baiknya mengukur kali ini sebelum melakukan pekerjaan threading yang relatif kompleks?

Jika Anda perlu utas, saya akan membuat utas 'n' untuk melakukan penggandaan sel (mungkin 'n' adalah jumlah inti yang tersedia untuk Anda), dan kemudian menggunakan mekanisme ExecutorService dan Masa Depan untuk mengirim beberapa perkalian secara bersamaan. .

Dengan begitu Anda dapat mengoptimalkan pekerjaan berdasarkan jumlah inti, dan Anda menggunakan alat threading Java tingkat tinggi (yang seharusnya membuat hidup lebih mudah). Tulis hasilnya kembali ke dalam matriks penerima, dan kemudian cukup cetak ini setelah semua tugas Masa Depan Anda selesai.


1
+1 @Greg Saya pikir Anda harus melihat pada paket java.util.concurrent, seperti yang ditunjukkan oleh Brian.
ATorras

1
+1 dan juga memeriksa buku ini yang juga akan mengajarkan Anda cara yang benar untuk menggunakan wait () dan notify () jcip.net
Chii

14

Katakanlah Anda memiliki aplikasi 'kotak hitam' dengan beberapa kelas bernama BlackBoxClassyang memiliki metodedoSomething(); .

Selanjutnya, Anda memiliki nama pengamat atau pendengar onResponse(String resp)yang akan dipanggil olehBlackBoxClass setelah waktu yang tidak diketahui.

Alurnya sederhana:

private String mResponse = null; 
 ...
BlackBoxClass bbc = new BlackBoxClass();
   bbc.doSomething();
...
@override
public void onResponse(String resp){        
      mResponse = resp;       
}

Katakanlah kita tidak tahu apa yang sedang terjadi BlackBoxClassdan kapan kita harus mendapatkan jawaban tetapi Anda tidak ingin melanjutkan kode Anda sampai Anda mendapatkan jawaban atau dengan kata lain mendapat onResponsepanggilan. Di sini memasuki 'Sinkronisasi pembantu':

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

Sekarang kita dapat menerapkan apa yang kita inginkan:

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

Anda hanya dapat memanggil pemberitahuan pada objek di mana Anda memiliki monitornya. Jadi kamu butuh sesuatu seperti

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


3

Saya akan menunjukkan contoh sederhana kepada Anda cara yang tepat untuk menggunakan waitdan notifydi Jawa. Jadi saya akan membuat dua kelas bernama ThreadA & ThreadB . ThreadA akan memanggil 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);
        }
    }
} 

dan untuk Kelas 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
            }
        }
    }

3

Penggunaan sederhana jika Anda ingin Cara menjalankan utas sebagai alternatif: -

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

respon: -

T1:A
T2:B
T1:A
T2:B
T1:A
T2:B
T1:A
T2:B
T1:A
T2:B
T1:A
T2:B

Bagaimana cara kerjanya ketika saya memiliki 4 operasi untuk melakukan secara sinkron?
saksham agarwal

2

kita dapat memanggil notifikasi untuk melanjutkan eksekusi objek yang menunggu sebagai

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!");
}

melanjutkan ini dengan memohon pemberitahuan pada objek lain dari kelas yang sama

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

0

Untuk masalah khusus ini, mengapa tidak menyimpan berbagai hasil Anda dalam variabel dan kemudian ketika thread terakhir Anda diproses, Anda dapat mencetak dalam format apa pun yang Anda inginkan. Ini sangat berguna jika Anda akan menggunakan riwayat pekerjaan Anda di proyek lain.


0

Ini terlihat seperti situasi untuk pola produsen-konsumen. Jika Anda menggunakan java 5 atau lebih tinggi, Anda dapat mempertimbangkan untuk menggunakan antrian pemblokiran (java.util.concurrent.BlockingQueue) dan membiarkan pekerjaan koordinasi utas dengan kerangka kerja dasar / implementasi api. Lihat contoh dari java 5: http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/BlockingQueue.html atau java 7 (contoh yang sama): http: // docs. oracle.com/javase/7/docs/api/java/util/concurrent/BlockingQueue.html


0

Anda telah menjaga dengan benar blok kode Anda saat Anda memanggil wait()metode dengan menggunakansynchronized(this) .

Tetapi Anda belum mengambil tindakan pencegahan yang sama ketika Anda memanggil notify()metode tanpa menggunakan blok yang dijaga:synchronized(this) atausynchronized(someObject)

Jika Anda lihat halaman dokumentasi oracle pada Object kelas, yang berisi wait(), notify(), notifyAll()metode, Anda bisa lihat di bawah tindakan pencegahan di semua tiga metode ini

Metode ini seharusnya hanya dipanggil oleh utas yang merupakan pemilik monitor objek ini

Banyak hal telah diubah dalam 7 tahun terakhir dan mari kita lihat alternatif lain untuk synchronizedpertanyaan SE di bawah ini:

Mengapa menggunakan ReentrantLock jika seseorang dapat menggunakan sinkronisasi (ini)?

Sinkronisasi vs Kunci

Hindari disinkronkan (ini) di Jawa?

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.