2017-03-24 2 views
1

Ich versuche zu verstehen, wie Javas Methoden wait und notify funktionieren. Gemäß der Dokumentation, wait() Ursachen Thread für nachfolgende Aufrufe von notify() oder notifyAll() Methoden warten, aber aus irgendeinem Grund nicht notify „warten“ nicht unterbricht:Die Methoden wait() und notify() verstehen

public static void main(String[] args) { 

    Thread thread1 = new Thread(new Runnable() { 
     @Override 
     public void run() { 
      System.out.println("thread1 is started, waiting for notify()"); 

      synchronized (this) { 
       try { 
        wait(); 
       } catch (InterruptedException e) { 
        System.out.println(e.getLocalizedMessage()); 
       } 
      } 

      System.out.println("waiting is over"); 
     } 
    }); 

    thread1.start(); 

    // unblock thread1 in 2 seconds 

    try { 
     Thread.sleep(2000); 
    } catch (InterruptedException e) { 
     e.printStackTrace(); 
    } 

    synchronized (thread1) { 
     thread1.notify(); 
    } 
} 
+2

Sie müssen 'wait()' und 'notify()' auf demselben Objekt aufrufen. Sie tun das nicht in Ihrem Code - Sie rufen implizit 'wait()' auf dem 'Runnable'-Objekt und' notify() 'auf dem' Thread'-Objekt auf. – Jesper

+1

'wait()' und 'notify()' sind Low-Level-Methoden, die auf sehr spezifische Weise zur Implementierung von Synchronisationsklassen auf höherer Ebene verwendet werden. https://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html Sie werden wahrscheinlich in Schwierigkeiten geraten, wenn Sie sie anders verwenden. In den meisten Fällen können Sie auch die Synchronisierungsklassen auf höherer Ebene verwenden, die vom 'java.util.concurrent'-Paket bereitgestellt werden, und die mit der Erstellung Ihrer eigenen verbundenen Risiken vermeiden. –

+0

Bezogen auf http://Stackoverflow.com/questions/17840397/concept-behind-putting-wait-notify-methods-in-object-class/17841450#17841450 – Gray

Antwort

4

Sie müssen das Objekt notify, die auf wait ed sein wird nicht der Thread, der wartet.

In Ihrem Fall ist das Objekt wait ed on eine Instanz einer anonymen inneren Klasse, was problematisch ist, weil Sie nicht einfach einen Verweis darauf erhalten können, um notify it. Sie könnten dies lösen, indem Thread direkt erstreckt:

Thread thread1 = new Thread() { 
    @Override 
    public void run() { 
     System.out.println("thread1 is started, waiting for notify()"); 

     synchronized (this) { 
      try { 
       wait(); 
      } catch (InterruptedException e) { 
       System.out.println(e.getLocalizedMessage()); 
      } 
     } 

     System.out.println("waiting is over"); 
    } 
}; 

Nun ist die this (in synchronized (this)) auf das Gewinde selbst bezieht, und die wait auch auf dem Thread-Objekt aufgerufen wird. In diesem Fall sollte Ihr aktueller Anruf an notify in Ordnung sein, da es das gleiche Objekt benachrichtigt (was passiert in diesem Fall ist der Thread, der wartet - aber um klar zu sein, muss das nicht der Fall sein).

Es ist nicht empfehlenswert, ein Objekt für die Synchronisation zu verwenden, das auch anderweitig verwendet werden kann; Instanzen Thema wären ein Beispiel dafür, und in der Tat die documentation rät ausdrücklich dagegen:

Es wird empfohlen, Anwendungen wait, nicht notify oder notifyAll auf Thread Instanzen verwenden.

Auch sollten Sie richtig handhaben Spurious Wakeup; das heißt, wait kann zurückgeben, weil notify/notifyAll an anderer Stelle aufgerufen wurde oder vielleicht gar nicht aufgerufen wurde. Wie die Dokumentation also says:

Ein Thread kann auch aufwachen, ohne benachrichtigt, unterbrochen oder Zeitüberschreitung, ein so genanntes Spurious Wakeup. Während dies in der Praxis selten vorkommt, müssen Anwendungen dagegen vorgehen, indem sie nach der Bedingung suchen, die den Thread geweckt haben sollte, und weiter warten, wenn die Bedingung nicht erfüllt ist. Mit anderen Worten, wartet immer in Schleifen [...]

Daher Ihr Beispiel wirklich eine separate Variable verwenden sollte zu verfolgen, ob die Wakeup beabsichtigt war (aufgrund einer expliziten notify) oder nicht auftreten sollte.

+0

Ja, das war der Fall - ich habe die Tatsache völlig übersehen, dass 'this' auf die anonyme' Runnable' Klasse und nicht auf die 'thread1' Variable verweist. Vielen Dank! – fraggjkee

+0

Die Verwendung von 'thread.wait()' und 'thread.notify()' wird normalerweise nicht empfohlen, da die 'Thread'-Klasse selbst' wait() 'und' notify() 'auf' Thread'-Objekten verwendet. –

1

aus irgendeinem Grund benachrichtigen „warten“ nicht unterbrechen:

@ davmac Antwort korrekt ist, aber für die Nachwelt, gibt es einige andere Möglichkeiten, wie Sie es Thread weil erstreckt und ruft wait() und notify() tun können auf dem Thread Objekt wird nicht empfohlen.

Der beste Weg wäre, ein Sperrobjekt zu erstellen.Das Erstellen Ihrer Sperrobjekte final ist immer ein gutes Muster, obwohl es auch hier in der inneren Klasse verwendet werden muss.

final Object lock = new Object(); 
Thread thread1 = new Thread(new Runnable() { 
    ... 
     synchronized (lock) { 
      try { 
       lock.wait(); 
      } catch (InterruptedException e) { 
       // always a good pattern 
       Thread.currentThread().interrupt(); 
       System.out.println(e.getLocalizedMessage()); 
      } 
     } 
     ... 
} 
... 
synchronized (lock) { 
    lock.notify(); 
} 
// might as well wait for it to finish 
thread1.join(); 
Verwandte Themen