IllegalMonitorStateException pada panggilan tunggu ()

162

Saya menggunakan multi-threading di java untuk program saya. Saya telah menjalankan utas dengan sukses tetapi ketika saya menggunakan Thread.wait(), itu sedang melempar java.lang.IllegalMonitorStateException. Bagaimana saya bisa membuat utas menunggu sampai diberitahukan?

prakash.panjwani
sumber
2
Thread.wait () tidak ada, mungkin this.wait ()
Premraj

Jawaban:

175

Anda harus berada di synchronizedblok agarObject.wait() dapat bekerja.

Juga, saya sarankan melihat paket concurrency daripada paket threading sekolah lama. Mereka lebih aman dan lebih mudah untuk diajak bekerja sama .

Selamat coding.

EDIT

Saya berasumsi Anda maksud Object.wait()sebagai pengecualian Anda adalah apa yang terjadi ketika Anda mencoba untuk mendapatkan akses tanpa memegang kunci objek.

reccles
sumber
1
tangkapan yang bagus. Saya berasumsi dia maksud Object.wait () dan dipanggil dari utas
reccles
2
Blok yang disinkronkan pada objek yang Anda tunggu. Ingin mengedit jawaban ini untuk membuatnya sedikit lebih jelas? Terima kasih.
Gray
55

waitdidefinisikan dalam Object, dan bukan itu Thread. Monitor menyala Threadsedikit tidak terduga.

Meskipun semua objek Java memiliki monitor, umumnya lebih baik memiliki kunci khusus:

private final Object lock = new Object();

Anda dapat sedikit lebih mudah membaca diagnostik, dengan biaya memori yang kecil (sekitar 2K per proses) dengan menggunakan kelas bernama:

private static final class Lock { }
private final Object lock = new Lock();

Untuk waitatau notify/ notifyAllsuatu objek, Anda harus memegang kunci dengan synchronizedpernyataan. Selain itu, Anda akan memerlukan whileperulangan untuk memeriksa kondisi bangun (temukan teks yang bagus di threading untuk menjelaskan alasannya).

synchronized (lock) {
    while (!isWakeupNeeded()) {
        lock.wait();
    }
}

Untuk memberi tahu:

synchronized (lock) {
    makeWakeupNeeded();
    lock.notifyAll();
}

Sebaiknya Anda memahami bahasa Jawa dan java.util.concurrent.lockskunci (dan java.util.concurrent.atomic) ketika masuk ke multithreading. Tetapi gunakan java.util.concurrentstruktur data kapan pun Anda bisa.

Tom Hawtin - tackline
sumber
5
Saya tidak pernah mengerti bagaimana ini bekerja, mengingat bahwa menunggu dan memberitahukan keduanya dalam blok yang disinkronkan pada objek yang sama (kunci). Karena utas tunggu ada di dalam blok, bukankah seharusnya itu membuat blok pemberitahuan pada baris "disinkronkan (kunci)"?
Brent212
6
@ Brent212 Untuk metode apa pun selain wait, ya Anda tidak akan pernah bisa notify. Namun, dalam dokumen API untuk Object.wait, "Utas melepaskan kepemilikan monitor ini". Jadi sementara di waitdalamnya seolah-olah berada di luar synchronizedblok melampirkan (untuk objek yang sama, mungkin beberapa synchronizedblok pada objek yang sama).
Tom Hawtin - tackline
24

Saya tahu utas ini sudah hampir 2 tahun tetapi masih harus ditutup karena saya juga datang ke sesi tanya jawab ini dengan masalah yang sama ...

Harap baca definisi illegalMonitorException ini lagi dan lagi ...

IllegalMonitorException dilemparkan untuk menunjukkan bahwa utas telah mencoba untuk menunggu di monitor objek atau untuk memberi tahu utas lain yang menunggu di monitor objek tanpa memiliki monitor yang ditentukan.

Baris ini berulang-ulang mengatakan, IllegalMonitorException muncul ketika salah satu dari 2 situasi terjadi ....

1> menunggu monitor objek tanpa memiliki monitor yang ditentukan.

2> beri tahu utas lain yang menunggu di monitor objek tanpa memiliki monitor yang ditentukan.

Beberapa mungkin sudah mendapatkan jawaban mereka ... yang semuanya tidak, maka silakan periksa 2 pernyataan ....

disinkronkan (objek)

object.wait ()

Jika kedua objek sama ... maka ilegalMonitorException tidak dapat datang.

Sekarang lagi baca definisi IllegalMonitorException dan Anda tidak akan melupakannya lagi ...

Mina
sumber
Sebenarnya, itu tidak berhasil. Saya sudah mencobanya. Saya membuat Runnable, menguncinya (menggunakan blok tersinkronisasi) dan di dalam blok itu saya menjalankan Runnable pada UI-thread (Android) dan setelah itu saya melakukan myRunnable.wait (), dan saya masih mendapatkan pengecualian.
Ted
Penjelasan Excelente !! Saya melakukan wait () tanpa menentukan objek, jadi butuh instance, dan sinkronisasi pada objek lain. Sekarang saya menggunakan otherObject.wait () dan berfungsi!
Fersca
6

Berdasarkan komentar Anda, sepertinya Anda melakukan sesuatu seperti ini:

Thread thread = new Thread(new Runnable(){
    public void run() { // do stuff }});

thread.start();
...
thread.wait();

Ada tiga masalah.

  1. Seperti yang dikatakan orang lain, obj.wait()hanya dapat dipanggil jika utas saat ini menyimpan kunci primitif / mutex obj. Jika utas saat ini tidak menahan kunci, Anda mendapatkan pengecualian yang Anda lihat.

  2. The thread.wait()panggilan tidak melakukan apa yang Anda tampaknya akan mengharapkan untuk melakukannya. Secara khusus, thread.wait() tidak menyebabkan utas yang dinominasikan menunggu. Sebaliknya itu menyebabkan utas saat ini menunggu sampai beberapa utas lainnya menelepon thread.notify()atauthread.notifyAll() .

    Sebenarnya tidak ada cara aman untuk memaksa sebuah Threadinstance untuk berhenti jika tidak mau. (Yang terdekat dengan Java adalah yang sudah usangThread.suspend() metode yang , tetapi metode itu secara inheren tidak aman, seperti yang dijelaskan dalam Javadoc.)

    Jika Anda ingin yang baru mulai Threadberhenti, cara terbaik untuk melakukannya adalah membuat CountdownLatchinstance dan membuat panggilan thread await()pada kait untuk menjeda sendiri. Utas utama kemudian akan memanggil countDown()kait untuk membiarkan utas yang dijeda berlanjut.

  3. Orthogonal ke poin sebelumnya, menggunakan Threadobjek sebagai kunci / mutex dapat menyebabkan masalah. Misalnya, javadoc untuk Thread::joinmengatakan:

    Implementasi ini menggunakan loop this.waitpanggilan yang dikondisikan this.isAlive. Saat utas berakhir, this.notifyAllmetode dipanggil. Disarankan bahwa aplikasi tidak menggunakan wait, notifyatau notifyAllpada Threadkasus.

Stephen C
sumber
2

Karena Anda belum memposting kode, kami agak bekerja dalam kegelapan. Apa rincian pengecualiannya?

Apakah Anda menelepon Thread.wait () dari dalam utas, atau di luarnya?

Saya menanyakan hal ini karena menurut javadoc untuk IllegalMonitorStateException, itu adalah:

Dilemparkan untuk menunjukkan bahwa utas telah mencoba untuk menunggu pada monitor objek atau untuk memberi tahu utas lainnya menunggu pada monitor objek tanpa memiliki monitor yang ditentukan.

Untuk memperjelas jawaban ini, panggilan untuk menunggu di utas ini juga melempar IllegalMonitorStateException, meskipun dipanggil dari dalam blok yang disinkronkan:


     private static final class Lock { }
     private final Object lock = new Lock();

    @Test
    public void testRun() {
        ThreadWorker worker = new ThreadWorker();
        System.out.println ("Starting worker");
        worker.start();
        System.out.println ("Worker started - telling it to wait");
        try {
            synchronized (lock) {
                worker.wait();
            }
        } catch (InterruptedException e1) {
            String msg = "InterruptedException: [" + e1.getLocalizedMessage() + "]";
            System.out.println (msg);
            e1.printStackTrace();
            System.out.flush();
        }
        System.out.println ("Worker done waiting, we're now waiting for it by joining");
        try {
            worker.join();
        } catch (InterruptedException ex) { }

    }
CPerkins
sumber
@CPerkins: Saya pikir Anda membingungkan utas eksekusi dan objek yang menjadi target wait().
Robert Munteanu
@ Robert - Mungkin saya, tapi saya rasa tidak. Jika Anda memulai contoh Thread, dan kemudian meminta untuk menunggu, Anda akan mendapatkan IllegalMonitorStateException, yang saya coba jelaskan.
CPerkins
Apakah Anda berbicara tentang worker.wait()garis? Maka Anda harus melakukan sinkronisasi pada pekerja, bukan pada kunci.
Robert Munteanu
1

Untuk berurusan dengan IllegalMonitorStateException, Anda harus memverifikasi bahwa semua doa tunggu, beri tahu, dan beri tahu semua metode hanya terjadi ketika utas panggilan memiliki monitor yang sesuai . Solusi paling sederhana adalah dengan melampirkan panggilan-panggilan ini di dalam blok yang disinkronkan. Objek sinkronisasi yang akan dipanggil dalam pernyataan yang disinkronkan adalah yang monitornya harus diperoleh.

Berikut adalah contoh sederhana untuk memahami konsep monitor

public class SimpleMonitorState {

    public static void main(String args[]) throws InterruptedException {

        SimpleMonitorState t = new SimpleMonitorState();
        SimpleRunnable m = new SimpleRunnable(t);
        Thread t1 = new Thread(m);
        t1.start();
        t.call();

    }

    public void call() throws InterruptedException {
        synchronized (this) {
            wait();
            System.out.println("Single by Threads ");
        }
    }

}

class SimpleRunnable implements Runnable {

    SimpleMonitorState t;

    SimpleRunnable(SimpleMonitorState t) {
        this.t = t;
    }

    @Override
    public void run() {

        try {
            // Sleep
            Thread.sleep(10000);
            synchronized (this.t) {
                this.t.notify();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
Rakesh Chaudhari
sumber
0

Panggilan Thread.wait () masuk akal di dalam kode yang disinkronkan pada objek Thread.class. Saya tidak berpikir itu yang Anda maksudkan.
Anda bertanya

Bagaimana saya bisa membuat utas menunggu sampai diberitahukan?

Anda hanya dapat membuat utas saat ini menunggu. Utas lainnya hanya dapat diminta dengan lembut untuk menunggu, jika disetujui.
Jika Anda ingin menunggu beberapa kondisi, Anda memerlukan objek kunci - objek Thread.class adalah pilihan yang sangat buruk - itu adalah AFAIK tunggal sehingga menyinkronkannya (kecuali untuk metode statis Thread) berbahaya.
Detail untuk sinkronisasi dan menunggu sudah dijelaskan oleh Tom Hawtin. java.lang.IllegalMonitorStateExceptionberarti Anda mencoba untuk menunggu objek yang tidak Anda sinkronkan - itu ilegal untuk melakukannya.

Tadeusz Kopec
sumber
0

Tidak yakin apakah ini akan membantu orang lain atau tidak, tetapi ini adalah bagian kunci untuk memperbaiki masalah saya di pengguna "Tom Hawtin - tacklin" jawaban di atas:

synchronized (lock) {
    makeWakeupNeeded();
    lock.notifyAll();
}

Hanya fakta bahwa "kunci" dilewatkan sebagai argumen dalam sinkronisasi () dan juga digunakan dalam "kunci" .notifyAll ();

Setelah saya membuatnya di 2 tempat itu saya membuatnya bekerja

jp093121
sumber
0

Saya menerima beberapa IllegalMonitorStateExceptionsaat mencoba membangun utas dalam / dari classutas berbeda . Dalam java 8Anda dapat menggunakan lockfitur dari API Concurrency baru bukan dari synchronizedfungsi.

Saya sudah menyimpan objek untuk asynchronoustransaksi websocket di a WeakHashMap. Solusi dalam kasus saya adalah juga menyimpan lockobjek dalamConcurrentHashMapsynchronous balasan untuk . Catatan tersebut condition.await(tidak .wait).

Untuk menangani multi threading, saya menggunakan a Executors.newCachedThreadPool()untuk membuat kumpulan thread .

Stuart Cardall
sumber
0

Mereka yang menggunakan Java 7.0 atau versi di bawahnya dapat merujuk kode yang saya gunakan di sini dan berfungsi.

public class WaitTest {

    private final Lock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();

    public void waitHere(long waitTime) {
        System.out.println("wait started...");
        lock.lock();
        try {
            condition.await(waitTime, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        lock.unlock();
        System.out.println("wait ends here...");
    }

    public static void main(String[] args) {
        //Your Code
        new WaitTest().waitHere(10);
        //Your Code
    }

}
pak lelaki
sumber