Implementiere deine blockierende Warteschlange in Java

8

Ich weiß, dass diese Frage schon oft gestellt und beantwortet wurde, aber ich konnte einfach keinen Trick bei den im Internet gefundenen Beispielen finden, wie dies oder das eins

Diese beiden Lösungen prüfen, ob das Array / die Warteschlange / verknüpfte Liste der blockierenden Warteschlange in notifyAll wartende Threads in put() -Methode und umgekehrt in get() -Methoden leer ist. Ein Kommentar im zweiten Link unterstreicht diese Situation und erwähnt, dass dies nicht notwendig ist.

Die Frage ist also; Es scheint mir auch ein bisschen seltsam zu sein zu überprüfen, ob die Warteschlange leer ist voll, um alle wartenden Threads zu benachrichtigen. Irgendwelche Ideen?

Vielen Dank im Voraus.

    
tugcem 21.11.2013, 00:25
quelle

3 Antworten

12

Ich weiß, dass dies eine alte Frage ist, aber nachdem ich die Frage und die Antworten gelesen habe, konnte ich mir selbst nicht helfen, ich hoffe, Sie finden das nützlich.

Bei der Überprüfung, ob die Warteschlange tatsächlich voll oder leer ist, bevor andere wartende Threads benachrichtigt werden, fehlt etwas, bei dem beide Methoden put (T t) und T get() beide synchronized -Methoden sind, was bedeutet, dass nur ein Thread einen eingeben kann von diesen Methoden zu einem Zeitpunkt, aber dies wird nicht verhindern, dass sie zusammen arbeiten, also wenn ein Thread-a put (T t) Methode eingegeben hat ein anderer Thread-b kann noch eingeben und starten Sie die Anweisungen in T get() Methode vor Thread-a hat put (T t) verlassen, und so wird dieses double-checking design den Entwickler ein bisschen sicherer fühlen lassen, weil Sie nicht wissen können, ob der zukünftige CPU-Kontextwechsel wann oder wann passieren wird.

Ein besserer und empfohlener Ansatz ist die Verwendung von Reentrant Locks und Conditions :

// Ich habe den Quellcode von diesem Link

bearbeitet %Vor%

Bei Verwendung dieses Ansatzes ist double checking nicht erforderlich, da das lock -Objekt zwischen den beiden Methoden geteilt wird, dh nur ein Thread a oder b kann gleichzeitig eine dieser Methoden eingeben, im Gegensatz zu synchronisierten Methoden, die verschiedene Monitore erstellen und nur die Threads, die warten, weil die Warteschlange voll ist, werden benachrichtigt, wenn mehr Platz vorhanden ist, und das gleiche gilt für Threads, die warten, weil die Warteschlange leer ist. Dies führt zu einer besseren CPU-Auslastung. Sie können ein detaillierteres Beispiel mit dem Quellcode hier finden

    
Ayesh Qumhieh 14.09.2014 23:01
quelle
1

Ich denke logisch, es ist nicht schaden, diese zusätzliche Überprüfung vor notifyAll() zu machen.

Du kannst einfach notifyAll() setzen, sobald du etwas aus der Warteschlange gestellt hast. Alles wird noch funktionieren und dein Code ist kürzer. Es wird jedoch auch nicht überprüft, ob möglicherweise jemand wartet (indem Sie überprüfen, ob die Grenze der Warteschlange erreicht wird), bevor Sie notifyAll() aufrufen. Diese zusätzliche Logik spart unnötige notifyAll() Aufrufe.

Es hängt nur davon ab, ob Sie einen kürzeren und saubereren Code benötigen oder ob Ihr Code effizienter ausgeführt werden soll. (Habe nicht in notifyAll() 's Implementierung nachgeschaut. Wenn es eine billige Operation ist, wenn niemand wartet, ist der Leistungsgewinn für diese zusätzliche Überprüfung sowieso nicht offensichtlich)

    
Adrian Shum 21.11.2013 03:22
quelle
0

Der Grund, warum die Autoren notifyAll() benutzt haben, ist einfach: Sie hatten keine Ahnung, ob es nötig war oder nicht, also entschieden sie sich für die "sicherere" Option.

Im obigen Beispiel wäre es ausreichend, einfach notify() aufzurufen, da für jedes einzelne hinzugefügte Element unter allen Umständen nur ein Thread warten kann.

Dies wird deutlicher, wenn Ihre Warteschlange auch die Möglichkeit bietet, mehrere Elemente in einem Schritt wie addAll(Collection<T> list) hinzuzufügen, da in diesem Fall mehr als ein Thread auf eine leere Liste warten könnte, um genau zu sein: as viele Threads als Elemente wurden hinzugefügt.

Das notifyAll() verursacht jedoch im speziellen Einzelelement-Fall einen zusätzlichen Overhead, da viele Threads unnötig aufgeweckt werden und daher wieder in den Ruhezustand versetzt werden müssen, wodurch der Zugriff auf die Warteschlange in der Zwischenzeit blockiert wird. Das Ersetzen von notifyAll() durch notify() würde die Geschwindigkeit in diesem speziellen Fall verbessern .

Aber dann nicht mit warte / benachrichtigen und überhaupt synchronisiert, aber stattdessen das gleichzeitige Paket würde die Geschwindigkeit um viel mehr als jede intelligente warte / notify-Implementierung könnte jemals erreichen.

    
TwoThe 22.11.2013 13:24
quelle