IllegalMonitorStateException olmadan Java'da bekleme ve bildirim nasıl kullanılır?


129

2 matrisim var ve onları çarpmam ve ardından her hücrenin sonuçlarını yazdırmam gerekiyor. Bir hücre hazır olur olmaz onu yazdırmam gerekiyor, ancak örneğin [2] [0] hücresinin sonucu önce hazır olsa bile [0] [0] hücresini [2] [0] hücresinden önce yazdırmam gerekiyor . Bu yüzden siparişe göre yazdırmam gerekiyor. Bu yüzden benim fikrim, yazıcı iş parçacığının multiplyThreaddoğru hücrenin yazdırılmaya hazır olduğunu bildirene kadar beklemesini sağlamak ve ardından printerThreadhücreyi yazdırıp beklemeye geri dönmek vb.

Bu yüzden, çarpmayı yapan şu iş parçacığına sahibim:

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

Her hücrenin sonucunu yazdıran iş parçacığı:

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

Şimdi bana şu istisnaları atıyor:

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)

49. satır multiplyThread"notify ()" dır .. Senkronize edileni farklı kullanmam gerektiğini düşünüyorum ama nasıl olduğundan emin değilim.

Bu kodun çalışmasına yardım edebilecek biri varsa gerçekten minnettar olacağım.

Yanıtlar:


215

Notify () ' yı çağırabilmek için aynı nesne üzerinde senkronize etmeniz gerekir.

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

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

29
Bu while(!JobCompleted);seçenek genellikle kötü bir fikirdir çünkü CPU'nuzu% 100 bağlayarak aynı değişkeni sürekli kontrol eder ( buraya bakın )
Matt Lyons

5
while(!JobCompleted) Thread.sleep(5); bu sorun yok
BeniBela

15
Hala tamamen farklı bir şey olma sorunu var. Yoklama (bir koşulun karşılanıp karşılanmadığını, yani ne yaptığınızı tekrar tekrar kontrol etmek), söz konusu koşul değiştirilirse (yani cevapta ana hatlarıyla belirttiğim şey) genellikle bildirilmeye göre daha az tercih edilir.
Bombe

3
@huseyintugrulbuyukisik wait, mevcut iş parçacığının waitçağrılan nesne üzerinde bir kilidi olduğunda çağırabilirsiniz . Bir synchronizedblok veya senkronize bir yöntem kullanmanız tamamen size bağlıdır.
Bombe

1
@BeniBela Ama elbette daha yavaş olması beklenebilir (Hüseyin'in orijinal sorusuna göre).
Thomas

64

Java'da waitve notifyveya notifyAllyöntemlerini kullanırken aşağıdaki hususlar hatırlanmalıdır:

  1. Birden fazla iş parçacığının kilit beklemesini bekliyorsanız notifyAllbunun yerine kullanın notify.
  2. waitVe notifyyöntemler senkronize bir bağlamda çağrılmalıdır . Daha ayrıntılı bir açıklama için bağlantıya bakın.
  3. wait()Yöntemi her zaman bir döngü içinde çağırın çünkü birden fazla iş parçacığı bir kilit için bekliyorsa ve bunlardan biri kilidi alıp koşulu sıfırlarsa, diğer iş parçacıklarının tekrar beklemeleri gerekip gerekmediğini görmek için uyandıktan sonra koşulu kontrol etmeleri gerekir. işlemeye başlayabilir.
  4. Arama wait()ve notify()yöntem için aynı nesneyi kullanın ; her nesnenin kendi kilidi vardır, bu nedenle wait()A nesnesini ve notify()B nesnesini çağırmak bir anlam ifade etmeyecektir.

21

Buna hiç gerek yok mu? Matrislerinizin ne kadar büyük olduğunu ve bir iş parçacığının basarken diğerinin çarpma işleminin bir faydası olup olmadığını merak ediyorum.

Belki de nispeten karmaşık diş açma işini yapmadan önce bu zamanı ölçmeye değer mi?

Eğer onu iş parçacığına ihtiyacınız varsa, hücrelerin çarpımını gerçekleştirmek için 'n' iş parçacığı oluştururum (belki 'n' sizin için mevcut olan çekirdek sayısıdır) ve ardından ExecutorService ve Future'ı kullanırdım. aynı anda birden çok çarpımı göndermek mekanizmasını kullanırdım. .

Bu şekilde, çalışmayı çekirdek sayısına göre optimize edebilirsiniz ve daha yüksek seviyeli Java iş parçacığı araçlarını (hayatı kolaylaştıran) kullanırsınız. Sonuçları tekrar bir alma matrisine yazın ve ardından tüm Gelecekteki görevleriniz tamamlandıktan sonra bunu yazdırın.


1
+1 @Greg Brian'ın belirttiği gibi java.util.concurrent paketine bir göz atmanız gerektiğini düşünüyorum.
ATorras

1
1 ve ayrıca da (size kullanım bekleyin (doğru şekilde öğretmek) ve bildirir bu kitabı) kontrol jcip.net
Chii

14

Diyelim ki BlackBoxClassmetodu olan bir sınıf ile 'kara kutu' uygulamanız var doSomething();.

Ayrıca, bilinmeyen bir zamandan sonra onResponse(String resp)çağırılacak bir gözlemci veya dinleyiciniz var BlackBoxClass.

Akış basit:

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

Diyelim ki neler olup bittiğini BlackBoxClassve ne zaman cevap almamız gerektiğini bilmiyoruz, ancak cevap alana kadar veya başka bir deyişle onResponsearama gelene kadar kodunuza devam etmek istemiyorsunuz . Burada 'Eşitleme yardımcısı' yazar:

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

Şimdi istediğimizi uygulayabiliriz:

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

Yalnızca monitörlerine sahip olduğunuz nesneler için bildirim çağırabilirsiniz. Yani bir şeye ihtiyacın var

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


3

Birazdan basit bir örnek size kullanım için doğru yolu göstereceğiz waitve notifyJava. Yani adında iki sınıf oluşturacağız ThreadA & ThreadB . ThreadA, ThreadB'yi arayacak.

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

ve Sınıf ThreadB için:

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

Alternatif olarak iş parçacıklarının nasıl çalıştırılacağını istiyorsanız basit kullanım: -

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

tepki :-

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

Senkronize bir şekilde gerçekleştirmem gereken 4 işlem varken bu nasıl çalışır?
saksham agarwal

2

Bekleyen nesnelerin yürütülmesine devam etmek için notify'ı çağırabiliriz.

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

aynı sınıftaki başka bir nesnede notify'ı çağırarak bunu devam ettirin

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

0

Bu özel sorun için, neden çeşitli sonuçlarınızı değişkenlerde saklamıyorsunuz ve ardından iş parçacığınızın sonuncusu işlendiğinde istediğiniz formatta yazdırabilirsiniz. İş geçmişinizi başka projelerde kullanacaksanız bu özellikle yararlıdır.


0

Bu, üretici-tüketici düzeni için bir durum gibi görünüyor. Java 5 veya üstünü kullanıyorsanız, engelleme kuyruğunu (java.util.concurrent.BlockingQueue) kullanmayı düşünebilir ve iş parçacığı koordinasyon çalışmasını temel çerçeve / api uygulamasına bırakabilirsiniz. Java 5'teki örneğe bakın: http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/BlockingQueue.html veya java 7 (aynı örnek): http: // docs. oracle.com/javase/7/docs/api/java/util/concurrent/BlockingQueue.html


0

wait()Kullanarak yöntemi çağırdığınızda kod bloğunuzu düzgün bir şekilde korudunuzsynchronized(this) .

Ancak notify()korumalı blok kullanmadan yöntemi çağırdığınızda aynı önlemi almamışsınızdır:synchronized(this) veyasynchronized(someObject)

Üzerinde Oracle dokümanları sayfasına bakın Eğer nesne içeren sınıfın, wait(), notify(), notifyAll()yöntemleri, tüm bu üç yöntemin de önlem aşağıda görebilirsiniz

Bu yöntem yalnızca bu nesnenin monitörünün sahibi olan bir iş parçacığı tarafından çağrılmalıdır.

Son 7 yılda birçok şey değişti ve hadi diğer alternatiflere bakalım. synchronized aşağıdaki SE sorularının :

Senkronize (bu) kullanılabiliyorsa neden ReentrantLock kullanılsın?

Senkronizasyon vs Kilit

Java'da senkronize edilmekten (bu) kaçının?

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.