2010-04-12 12 views
7

Ich habe ein folgendes Programm:Legendes Java Warten und Benachrichtigen Methoden

import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 


public class SimpleWaitNotify implements Runnable { 

final static Object obj = new Object(); 
static boolean value = true; 

public synchronized void flag() { 
    System.out.println("Before Wait"); 
    try { 
     obj.wait(); 
    } catch (InterruptedException e) { 
     System.out.println("Thread interrupted"); 
    } 
    System.out.println("After Being Notified"); 
} 

public synchronized void unflag() { 
    System.out.println("Before Notify All"); 
    obj.notifyAll(); 
    System.out.println("After Notify All Method Call"); 
} 

public void run() { 
    if (value) { 
     flag(); 
    } else { 
     unflag(); 
    } 
} 

public static void main(String[] args) throws InterruptedException { 
    ExecutorService pool = Executors.newFixedThreadPool(4); 
    SimpleWaitNotify sWait = new SimpleWaitNotify(); 
    pool.execute(sWait); 
    SimpleWaitNotify.value = false; 
    SimpleWaitNotify sNotify = new SimpleWaitNotify(); 
    pool.execute(sNotify); 
    pool.shutdown(); 

} 

}

Wenn ich auf obj warten, erhalte ich die folgende Ausnahme Exception in thread "pool-1-thread-1" java.lang.IllegalMonitorStateException: current thread not owner für jeden der zwei Threads.

Aber wenn ich SimpleWaitNotify Monitor verwende, dann ist die Programmausführung ausgesetzt. Mit anderen Worten, ich denke, es unterbricht den aktuellen Ausführungs-Thread und wiederum den Executor. Jede Hilfe, um zu verstehen, was vor sich geht, würde gebührend gewürdigt werden.

Dies ist ein Bereich, in dem die Theorie und Javadoc einfach erscheinen, und da es nicht viele Beispiele gibt, hat konzeptionell eine große Lücke in mir hinterlassen.

Antwort

12

Du nennst wait und notifyAll auf obj, aber Sie Synchronisierung auf this (weil Sie synchronisierte Methoden haben).

Um zu warten oder zu benachrichtigen, müssen Sie den Monitor zuerst "besitzen". Desynchronisieren, die Methoden und synchronisiert auf obj statt:

public void flag() { 
    System.out.println("Before Wait"); 
    synchronized (obj) { 
     try { 
      obj.wait(); 
     } catch (InterruptedException e) { 
      System.out.println("Thread interrupted"); 
     } 
    } 
    System.out.println("After Being Notified"); 
} 

public void unflag() { 
    System.out.println("Before Notify All"); 
    synchronized (obj) { 
     obj.notifyAll(); 
    } 
    System.out.println("After Notify All Method Call"); 
} 
+0

Großartig. Kann man dasselbe erreichen, wenn man das zusammengesetzte Objekt 'obj' insgesamt loswird? – Maddy

+4

@Maddy: Sie sollten in der Lage sein zu warten und auf "dies" zu benachrichtigen - aber es ist in der Regel eine schlechte Idee, wie "dies" ist in der Regel eine Referenz, auf die anderen Code hat, effektiv zugreifen. Einen privaten Verweis auf ein Objekt zu halten, von dem nur Ihr Code weiß, ist eine gute Vorgehensweise. –

+0

Guter Punkt. Einverstanden. – Maddy

3

Entweder synchronize auf obj, oder rufen wait und notify auf this. Der aufrufende Thread muss den Monitor des selben Objekts enthalten, auf dem diese Methoden aufgerufen werden.

Zum Beispiel

synchronized void flag() { 
    System.out.println("Before Wait"); 
    try { 
    wait(); 
    } catch (InterruptedException e) { 
    System.out.println("Thread interrupted"); 
    } 
    System.out.println("After Being Notified"); 
} 

In diesem Beispiel wird die Sperre auf this gehalten (wenn der Modifikator synchronized auf einer Instanzmethode verwendet wird, wird der Monitor der Instanz erworben). So kann die wait()-Methode in der implizierten Instanz this aufgerufen werden.


Um die beiden Threads zu koordinieren, müssen sie die gleiche Sperre teilen. Die ursprüngliche Version hatte eine statische obj, die als Sperre verwendet werden konnte, aber es wurde nicht in den synchronized Blöcken verwendet. Hier ist ein besseres Beispiel:

class SimpleWaitNotify implements Runnable { 

    private final Object lock; 
    private final boolean wait; 

    SimpleWaitNotify(Object lock, boolean wait) { 
    this.lock = lock; 
    this.wait = wait; 
    } 

    public void flag() { 
    synchronized (lock) { 
     System.out.println("Before Wait"); 
     try { 
     lock.wait(); 
     System.out.println("After Being Notified"); 
     } catch (InterruptedException ex) { 
     System.out.println("Thread interrupted"); 
     } 
    } 
    } 

    public void unflag() { 
    synchronized(lock) { 
     System.out.println("Before Notify All"); 
     lock.notifyAll(); 
     System.out.println("After Notify All Method Call"); 
    } 
    } 

    public void run() { 
    if (wait) { 
     flag(); 
    } else { 
     unflag(); 
    } 
    } 

    public static void main(String[] argv) throws Exception { 
    ExecutorService pool = Executors.newFixedThreadPool(4); 
    Object shared = new Object(); 
    SimpleWaitNotify sWait = new SimpleWaitNotify(shared, true); 
    pool.execute(sWait); 
    SimpleWaitNotify sNotify = new SimpleWaitNotify(shared, false); 
    pool.execute(sNotify); 
    pool.shutdown(); 
    } 

} 
+0

Ich änderte stattdessen, um auf SimpleWait Objekt selbst zu warten. In diesem Fall wird das Programm jedoch nicht angehalten. Stattdessen wird der unterbrochene Thread nicht benachrichtigt. Ich bekomme Folgendes gedruckt. Vor dem Warten Vor dem Notify Alle Nach Notify Alle Methodenaufruf – Maddy

+0

@Maddy in Ordnung, sehe ich das Problem. Bitte überprüfe mein Update. – erickson

1

vielmehr die direkte Aussage setzen pool.shutdown(), versuchen Sie, wie unten.

while (!service.isTerminated()) 
{ 
    service.shutdown(); 
} 

So wird gewartet, bis die Ausführung aller Threads abgeschlossen ist.