424 Stimmen

Java: notify() vs. notifyAll() noch einmal von vorn

Googelt man nach "Unterschied zwischen notify() y notifyAll() ", dann werden viele Erklärungen erscheinen (abgesehen von den javadoc-Absätzen). Es läuft alles auf die Anzahl der wartenden Threads hinaus, die geweckt werden: einer in notify() und alle in notifyAll() .

Allerdings wird (wenn ich den Unterschied zwischen diesen Methoden richtig verstehe) immer nur ein Thread für die weitere Monitor-Erfassung ausgewählt; im ersten Fall derjenige, der von der VM ausgewählt wird, im zweiten Fall derjenige, der vom Thread-Scheduler des Systems ausgewählt wird. Die genauen Auswahlverfahren für beide (im allgemeinen Fall) sind dem Programmierer nicht bekannt.

Was ist das nützlich Unterschied zwischen notify() y notifyAll() dann? Übersehe ich etwas?

6 Stimmen

Die nützlichen Bibliotheken für die Gleichzeitigkeit sind in den Gleichzeitigkeitsbibliotheken zu finden. Ich schlage vor, dass diese in fast jedem Fall die bessere Wahl sind. Die Concurency-Bibliotheken stammen aus der Zeit vor Java 5.0 (wo sie 2004 als Standard hinzugefügt wurden)

4 Stimmen

Ich bin anderer Meinung als Peter. Die Gleichzeitigkeitsbibliothek ist in Java implementiert, und jedes Mal, wenn Sie lock(), unlock() usw. aufrufen, wird eine Menge Java-Code ausgeführt. Man kann sich in den Fuß schießen, wenn man die Gleichzeitigkeitsbibliothek anstelle der guten alten synchronized außer für bestimmte, eher seltene Anwendungsfälle.

4 Stimmen

Das Hauptmissverständnis scheint folgendes zu sein: ... wird immer nur ein Thread für die weitere Monitor-Erfassung ausgewählt; im ersten Fall der von der VM, im zweiten Fall der vom System-Thread-Scheduler ausgewählte. Dies impliziert, dass sie im Wesentlichen gleich sind. Das beschriebene Verhalten ist zwar korrekt, aber es fehlt, dass in der notifyAll() Fall bleiben _die anderen Threads nach dem ersten wach und übernehmen den Monitor, einer nach dem anderen. In dem notify Fall wird keiner der anderen Threads aufgeweckt. Funktionell sind sie also sehr unterschiedlich!

269voto

Liedman Punkte 9809

Allerdings wird (wenn ich den Unterschied zwischen diesen Methoden richtig verstehe) immer nur ein Thread für die weitere Monitor-Erfassung ausgewählt.

Das ist nicht richtig. o.notifyAll() weckt alle der Threads, die in den folgenden Bereichen blockiert sind o.wait() Anrufe. Die Threads dürfen nur zurückkehren von o.wait() einer nach dem anderen, aber sie alle wird an die Reihe kommen.


Ganz einfach: Es hängt davon ab, warum Ihre Threads auf eine Benachrichtigung warten. Wollen Sie einem der wartenden Threads mitteilen, dass etwas passiert ist, oder wollen Sie alle gleichzeitig informieren?

In einigen Fällen können alle wartenden Threads nützliche Aktionen durchführen, sobald die Wartezeit beendet ist. Ein Beispiel wäre eine Gruppe von Threads, die auf die Beendigung einer bestimmten Aufgabe warten; sobald die Aufgabe beendet ist, können alle wartenden Threads mit ihrer Arbeit fortfahren. In einem solchen Fall würden Sie Folgendes verwenden notifyAll() um alle wartenden Threads gleichzeitig aufzuwecken.

In einem anderen Fall, z. B. bei einer sich gegenseitig ausschließenden Sperre, kann nur einer der wartenden Threads etwas Sinnvolles tun, nachdem er benachrichtigt wurde (in diesem Fall die Sperre übernehmen). In einem solchen Fall sollten Sie lieber notify() . Richtig umgesetzt, können Sie podría verwenden. notifyAll() auch in dieser Situation, aber Sie würden unnötigerweise Threads aufwecken, die ohnehin nichts tun können.


In vielen Fällen wird der Code, der eine Bedingung abwarten soll, als Schleife geschrieben:

synchronized(o) {
    while (! IsConditionTrue()) {
        o.wait();
    }
    DoSomethingThatOnlyMakesSenseWhenConditionIsTrue_and_MaybeMakeConditionFalseAgain();
}

Auf diese Weise kann, falls ein o.notifyAll() Aufruf mehr als einen wartenden Thread auf, und der erste, der vom o.wait() die Bedingung im falschen Zustand belässt, werden die anderen Threads, die geweckt wurden, wieder in den Wartezustand versetzt.

36 Stimmen

Wenn Sie nur einen Thread benachrichtigen, aber mehrere auf ein Objekt warten, wie bestimmt die VM, welcher Thread benachrichtigt werden soll?

6 Stimmen

Ich kann nicht mit Sicherheit sagen, wie die Java-Spezifikation aussieht, aber im Allgemeinen sollten Sie es vermeiden, Annahmen über solche Details zu treffen. Ich denke, Sie können davon ausgehen, dass die VM dies auf eine vernünftige und meist faire Weise tun wird.

0 Stimmen

Möchten Sie einem der wartenden Threads mitteilen, dass etwas passiert ist, oder möchten Sie es allen gleichzeitig mitteilen? Gute Erläuterung

50voto

David Crow Punkte 15779

Nützliche Unterschiede:

  • Utilice notify() wenn alle wartenden Threads austauschbar sind (die Reihenfolge, in der sie aufwachen, spielt keine Rolle), oder wenn Sie immer nur einen wartenden Thread haben. Ein gängiges Beispiel ist ein Thread-Pool, der für die Ausführung von Aufträgen aus einer Warteschlange verwendet wird - wenn ein Auftrag hinzugefügt wird, wird einer der Threads benachrichtigt, aufzuwachen, den nächsten Auftrag auszuführen und sich wieder schlafen zu legen.

  • Utilice notifyAll() für andere Fälle, in denen die wartenden Threads unterschiedliche Zwecke haben und gleichzeitig laufen können sollten. Ein Beispiel ist ein Wartungsvorgang für eine gemeinsam genutzte Ressource, bei dem mehrere Threads auf den Abschluss des Vorgangs warten, bevor sie auf die Ressource zugreifen.

19voto

andyuk Punkte 36182

Ich denke, es hängt davon ab, wie die Ressourcen produziert und verbraucht werden. Wenn 5 Arbeitsobjekte gleichzeitig verfügbar sind und Sie 5 Verbraucherobjekte haben, wäre es sinnvoll, alle Threads mit notifyAll() aufzuwecken, damit jeder ein Arbeitsobjekt verarbeiten kann.

Wenn Sie nur ein einziges Arbeitsobjekt zur Verfügung haben, welchen Sinn hat es dann, alle Verbraucherobjekte aufzuwecken, um um dieses eine Objekt zu kämpfen? Der erste, der nach verfügbarer Arbeit sucht, wird sie bekommen und alle anderen Threads werden feststellen, dass sie nichts zu tun haben.

Ich habe eine tolle Erklärung hier . Kurz gesagt:

Die Methode notify() wird im Allgemeinen verwendet für Ressourcenpools , wo es eine beliebige Anzahl von "Verbrauchern" oder "Arbeiter" gibt, die Ressourcen aufnehmen, aber wenn eine Ressource zum Pool hinzugefügt wird, nur einer der wartenden Konsumenten oder Arbeiter mit ihr umgehen können. Die notifyAll()-Methode wird eigentlich in den meisten anderen Fällen verwendet. Streng genommen, ist es erforderlich, um die Wartenden über eine Bedingung zu benachrichtigen, die es mehreren Wartende fortfahren können. Aber das ist oft schwer zu wissen. Daher gilt als allgemeine Regel, wenn Sie keine besondere Logik für die Verwendung von notify() haben, dann sollten Sie wahrscheinlich notifyAll() verwenden , denn es ist oft schwierig zu wissen welche Threads genau auf ein bestimmtes Objekt warten auf ein bestimmtes Objekt warten und warum.

12voto

Beachten Sie, dass Sie bei Gleichzeitigkeitsprogrammen auch die Wahl haben zwischen signal() y signalAll() da diese Methoden dort aufgerufen werden. Die Frage bleibt also auch bei java.util.concurrent .

Doug Lea bringt einen interessanten Punkt in seinem berühmtes Buch : wenn ein notify() y Thread.interrupt() zur gleichen Zeit geschehen, könnte die Meldung tatsächlich verloren gehen. Wenn dies passieren kann und dramatische Auswirkungen hat notifyAll() ist die sicherere Wahl, auch wenn Sie den Preis des Overheads zahlen (meistens werden zu viele Threads aufgeweckt).

10voto

Erik Punkte 101

Hier ist ein Beispiel. Führen Sie es aus. Dann ändern Sie eine der notifyAll() in notify() und sehen Sie, was passiert.

ProducerConsumerExample-Klasse

public class ProducerConsumerExample {

    private static boolean Even = true;
    private static boolean Odd = false;

    public static void main(String[] args) {
        Dropbox dropbox = new Dropbox();
        (new Thread(new Consumer(Even, dropbox))).start();
        (new Thread(new Consumer(Odd, dropbox))).start();
        (new Thread(new Producer(dropbox))).start();
    }
}

Dropbox-Klasse

public class Dropbox {

    private int number;
    private boolean empty = true;
    private boolean evenNumber = false;

    public synchronized int take(final boolean even) {
        while (empty || evenNumber != even) {
            try {
                System.out.format("%s is waiting ... %n", even ? "Even" : "Odd");
                wait();
            } catch (InterruptedException e) { }
        }
        System.out.format("%s took %d.%n", even ? "Even" : "Odd", number);
        empty = true;
        notifyAll();

        return number;
    }

    public synchronized void put(int number) {
        while (!empty) {
            try {
                System.out.println("Producer is waiting ...");
                wait();
            } catch (InterruptedException e) { }
        }
        this.number = number;
        evenNumber = number % 2 == 0;
        System.out.format("Producer put %d.%n", number);
        empty = false;
        notifyAll();
    }
}

Klasse der Verbraucher

import java.util.Random;

public class Consumer implements Runnable {

    private final Dropbox dropbox;
    private final boolean even;

    public Consumer(boolean even, Dropbox dropbox) {
        this.even = even;
        this.dropbox = dropbox;
    }

    public void run() {
        Random random = new Random();
        while (true) {
            dropbox.take(even);
            try {
                Thread.sleep(random.nextInt(100));
            } catch (InterruptedException e) { }
        }
    }
}

Erzeugerklasse

import java.util.Random;

public class Producer implements Runnable {

    private Dropbox dropbox;

    public Producer(Dropbox dropbox) {
        this.dropbox = dropbox;
    }

    public void run() {
        Random random = new Random();
        while (true) {
            int number = random.nextInt(10);
            try {
                Thread.sleep(random.nextInt(100));
                dropbox.put(number);
            } catch (InterruptedException e) { }
        }
    }
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X