Wait () çağrısında IllegalMonitorStateException


162

Programım için Java'da çoklu iş parçacığı kullanıyorum. Ben başarıyla iş parçacığı çalıştırmak var ama ben kullanırken Thread.wait(), atıyor java.lang.IllegalMonitorStateException. Bildirilene kadar bir konuyu nasıl bekletebilirim?


2
Thread.wait () mevcut değil, this.wait ()
Premraj

Yanıtlar:


175

Çalışabilmek synchronizediçin bir blokta olmanız gerekir Object.wait().

Ayrıca, eski okul iş parçacığı paketleri yerine eşzamanlılık paketlerine bakmanızı öneririm. Daha güvenli ve birlikte çalışmak çok daha kolay .

Mutlu kodlama.

DÜZENLE

Object.wait()İstisna nesneler kilidini tutmadan erişim kazanmaya çalıştığınızda ne olduğu anlamına geldiğini varsaydım .


1
İyi yakalama. o Object.wait () demek ve bir iplik denilen kabul
08:00

2
Beklediğiniz nesne üzerinde eşitlenmiş bir blok. Bunu biraz daha açık hale getirmek için bu cevabı düzenlemeyi düşünüyor musunuz? Teşekkürler.
Gri

55

waitiçinde tanımlanmış Object, değil Thread. Monitör açık Threadbir şekilde tahmin edilemez.

Tüm Java nesnelerinin monitörleri olmasına rağmen, genellikle özel bir kilide sahip olmak daha iyidir:

private final Object lock = new Object();

Tanımlı bir sınıfı kullanarak, küçük bellek maliyetiyle (işlem başına yaklaşık 2K) tanılamayı okumak biraz daha kolay olabilir:

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

Amacıyla waitveya notify/ notifyAllbir nesne, sizinle kilit tutan gerekmektedir synchronizedaçıklamada. Ayrıca, whileuyandırma durumunu kontrol etmek için bir döngüye ihtiyacınız olacaktır (nedenini açıklamak için iplik üzerinde iyi bir metin bulun).

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

Bildirmek için:

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

Birden çok iş parçacığına girerken hem Java dilini hem de java.util.concurrent.lockskilitlerini (ve java.util.concurrent.atomic) anlamaya değer . Ancak, java.util.concurrentmümkün olduğunda veri yapılarını kullanın .


5
Bekle ve bildir aynı nesne üzerinde senkronize bloklar (kilit) olduğu göz önüne alındığında, bunun nasıl çalıştığını hiç anlamadım. Bekleme iş parçacığı blokta olduğundan, bu "eşitlenmiş (kilit)" satırındaki bildirim iş parçacığı blok yapmak değil mi?
Brent212

6
@ Brent212 Başka herhangi bir yöntem için wait, evet asla ulaşamazsınız notify. Bununla birlikte, Object.wait"İş parçacığı bu monitörün sahipliğini serbest bırakır" API belgelerinde . Bu yüzden waitkapalı synchronizedblokların dışındayken (aynı nesne için, aynı nesne synchronizedüzerinde birden fazla blok olabilir ).
Tom Hawtin - taktik hattı

24

Ben bu konu neredeyse 2 yaşında olduğunu biliyorum ama aynı sorunu ile bu Q / A oturumuna geldi beri hala bunu kapatmak gerekir ...

Lütfen illegalMonitorException'ın bu tanımını tekrar tekrar okuyun ...

IllegalMonitorException, bir iş parçacığının bir nesnenin monitöründe beklemeye çalıştığını veya bir nesnenin izleyicisini bekleyen diğer iş parçacıklarını belirtilen monitöre sahip olmadan bildirmeye çalıştığını belirtir.

Bu satır tekrar tekrar diyor, 2 durumdan biri meydana geldiğinde IllegalMonitorException geliyor ....

1> belirtilen monitöre sahip olmadan bir nesnenin monitöründe bekleyin.

2> belirtilen monitöre sahip olmadan bir nesnenin ekranında bekleyen diğer iş parçacıklarını bilgilendirin.

Bazılarının cevapları olabilir ... hepsi yok, lütfen 2 ifadeyi kontrol edin ....

senkronize (nesne)

object.wait ()

Her iki nesne de aynı ise ... illegalMonitorException gelemez.

Şimdi tekrar IllegalMonitorException tanımını okuyun ve tekrar unutmayacaksınız ...


Aslında bu işe yaramıyor. Denedim. Ben bir Runnable oluşturmak, kilitlemek (senkronize blok kullanarak) ve bu blok içinde ben UI iş parçacığında (Android) Runnable çalıştırın ve bundan sonra myRunnable.wait () yapmak ve hala istisna olsun.
Ted

Excelente açıklaması !! Ben nesneyi belirtmeden wait () yapıyordum, bu yüzden örnek aldı ve başka bir nesne üzerinde senkronize. Şimdi otherObject.wait () kullanıyorum ve işe yarıyor!
Fersca

6

Yorumlarınıza dayanarak böyle bir şey yapıyormuşsunuz gibi geliyor:

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

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

Üç sorun var.

  1. Diğerlerinin söylediği gibi obj.wait(), sadece mevcut iş parçacığı için ilkel kilit / muteks tutarsa ​​çağrılabilir obj. Geçerli iş parçacığı kilidi tutmazsa, gördüğünüz özel durumu alırsınız.

  2. thread.wait()İfadesi gerek yapmak bekliyor gibi görünüyor ne yapmaz. Özellikle, atanan iş parçacığının beklemesine neden thread.wait() olmaz . Bunun yerine , geçerli evre başka bir evre çağrılıncaya kadar bekler thread.notify()veyathread.notifyAll() .

    Aslında bir Threadörneği istemiyorsa duraklamaya zorlamanın güvenli bir yolu yoktur . (Java'nın buna en yakın olanı kullanımdan kaldırılmış Thread.suspend()yöntemdir, ancak Javadoc'ta açıklandığı gibi bu yöntem doğal olarak güvenli değildir.)

    Yeni başlamayı Threadduraklatmak istiyorsanız, bunu yapmanın en iyi yolu, bir CountdownLatchörnek oluşturmak ve await()kendini duraklatmak için mandalda iş parçacığı araması yapmaktır . Ana iş parçacığı countDown(), duraklatılmış iş parçacığının devam etmesi için mandalı çağırır .

  3. Önceki noktalara dik olarak, bir Threadnesneyi kilit / muteks olarak kullanmak sorunlara neden olabilir. Örneğin, javadoc Thread::joinşöyle diyor:

    Bu uygulama, this.waitkoşullandırılmış bir çağrı döngüsü kullanır this.isAlive. Bir iş parçacığı sona erdiğinde this.notifyAllyöntem çağrılır. Uygulamalar kullanmayın önerilir wait, notifyya notifyAllüzerinde Threaddurumlarda.


2

Kod göndermediğiniz için karanlıkta çalışıyoruz. İstisnanın detayları nelerdir?

Thread.wait () öğesini konu içinden mi yoksa dışından mı çağırıyorsunuz?

IllegalMonitorStateException için javadoc göre, bu soruyorum:

Bir iş parçacığının, bir nesnenin monitöründe beklemeye çalıştığını veya belirtilen monitöre sahip olmadan bir nesnenin ekranında bekleyen diğer iş parçacıklarını bilgilendirmeye çalıştığını göstermek için atılır.

Bu cevabı açıklığa kavuşturmak için, bir iş parçacığını beklemeye yönelik bu çağrı, senkronize edilmiş bir bloktan çağrılmasına rağmen IllegalMonitorStateException'ı da atar:


     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: Sanırım yürütme iş parçacığını ve hedefi olan nesneyi karıştırıyorsunuz wait().
Robert Munteanu

@Robert - Belki de öyleyim, ama sanmıyorum. Bir Thread örneğini başlatır ve sonra beklemesini isterseniz, açıklamaya çalıştığım şey olan bir IllegalMonitorStateException alırsınız.
CPerkins

Hattan mı bahsediyorsun worker.wait()? O zaman kilit üzerinde değil, işçi üzerinde senkronize olmalısınız.
Robert Munteanu

1

IllegalMonitorStateException ile başa çıkmak için, bekleme, bildirme ve bildirme işlemlerinin tüm çağrılarının yalnızca çağıran evre uygun monitöre sahip olduğunda gerçekleştiğini doğrulamalısınız. . En basit çözüm, bu çağrıları senkronize bloklara dahil etmektir. Senkronize ifadede çağrılacak olan senkronizasyon nesnesi, monitörü edinilmesi gereken nesnedir.

Monitör kavramını anlamak için basit bir örnek

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

0

Thread.lassit () çağrısı, Thread.class nesnesinde senkronize edilen bir kod içinde anlamlıdır. Ne demek istediğini sanmıyorum.
Sen sor

Bildirilene kadar bir konuyu nasıl bekletebilirim?

Yalnızca geçerli iş parçacığınızı bekletebilirsiniz. Başka herhangi bir iş parçacığının kabul etmesi durumunda sadece hafifçe beklemesi istenebilir.
Bir koşul beklemek istiyorsanız, bir kilit nesnesine ihtiyacınız vardır - Thread.class nesnesi çok kötü bir seçimdir - tek bir AFAIK'tir, bu nedenle üzerinde senkronize etmek (Thread statik yöntemleri hariç) tehlikelidir.
Senkronizasyon ve bekleme detayları Tom Hawtin tarafından zaten açıklanmıştır. java.lang.IllegalMonitorStateExceptionsenkronize edilmediğiniz bir nesneyi beklemeye çalıştığınız anlamına gelir - bunu yapmak yasa dışıdır.


0

Bunun bir başkasına yardım edip etmeyeceğinden emin değilim ama bu, yukarıdaki kullanıcı "Tom Hawtin - tacklin" in cevabındaki sorunumu çözmenin kilit kısmıydı:

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

"Kilit" öğesinin senkronize () öğesinde argüman olarak iletilmesi ve "kilit" öğesinde de kullanılması .notifyAll ();

Bir kez bu 2 yerde yaptım Çalıştırdım


0

IllegalMonitorStateExceptionFarklı bir class/ iş parçacığında / gelen bir iş parçacığını uyandırmaya çalışırken bir süre aldım . In java 8size kullanabilirsiniz lockyeni eşzamanlılık API özelliklerini yerine ait synchronizedfonksiyonlar.

Zaten asynchronouswebsocket işlemleri için nesneleri bir WeakHashMap. Benim durumumdaki çözüm aynı zamanda bir locknesneyiConcurrentHashMapsynchronous yanıtlar için saklamaktı . Notcondition.await (değil .wait).

Çoklu iş parçacığı işlemek için Executors.newCachedThreadPool()bir iş parçacığı havuzu oluşturmak için a kullandım .


0

Java 7.0 veya önceki sürümleri kullananlar, burada kullandığım koda başvurabilir ve çalışır.

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
    }

}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.