Java-Thread-Ping-Pong-Beispiel
Ich versuche zu verstehen Gewinde Grundlagen, und als erstes Beispiel erstelle ich zwei thread, die schreiben, die einen String auf der Standardausgabe ausgegeben. Ich weiß der scheduler ermöglicht die Ausführung der threads mit einer round-robin-Zeitplan. Thats, warum ich habe:
PING
PING
pong
pong
pong
PING
PING
PING
pong
pong
Nun möchte ich eine shared-variable, so dass jeder thread wird wissen, wenn seine Ihre umdrehung:
public class PingPongThread extends Thread {
private String msg;
private static String turn;
public PingPongThread(String msg){
this.msg = msg;
}
@Override
public void run() {
while(true) {
playTurn();
}
}
public synchronized void playTurn(){
if (!msg.equals(turn)){
turn=msg;
System.out.println(msg);
}
}
}
Main-Klasse:
public class ThreadTest {
public static void main(String[] args) {
PingPongThread thread1 = new PingPongThread("PING");
PingPongThread thread2 = new PingPongThread("pong");
thread1.start();
thread2.start();
}
}
Ich synchronisiert, die "turn-manager", aber ich bekomme immer noch so etwas wie:
PING
PING
pong
pong
pong
PING
PING
PING
pong
pong
Kann jemand erklärt, was ich bin fehlt, und Warum bekomme ich nicht die Ping-pong... ping pong.
Danke!
Von "biegen-manager" meine ich public synchronized void playTurn()
InformationsquelleAutor Kummo | 2012-10-08
Du musst angemeldet sein, um einen Kommentar abzugeben.
Diese Zeile:
entspricht im Verhalten zu
deshalb keine Synchronisation auftreten, denn wie Brian Agnew darauf hingewiesen, daß die threads synchronisiert werden, die auf zwei unterschiedliche Objekte (thread1, Thread2 verzahnt), jeweils auf einer eigenen Instanz, wodurch keine effektive Synchronisierung.
Wenn Sie an der Reihe variable für Synchronisation, z.B.:
dann ist die situation viel besser ist (die mehrmals ausgeführt werden, um zu überprüfen), aber es gibt auch keine 100% Synchronisation. Im Anfang (meist) erhalten Sie einen doppelten ping und Doppel-pong, und danach sehen Sie synchronisiert, aber man kann immer noch erhalten die doppelte pings/pongs.
Synchronisierten block sperren auf Wert (siehe diese tolle Antwort) und nicht die Verweis auf den Wert. (siehe EDIT)
Werfen wir also einen Blick auf ein mögliches Szenario:
Um zu überprüfen, dass ich versucht, indem Sie
vor und nach
und es sieht synchronisiert?! Aber, wenn Sie
nach wenigen Sekunden sehen Sie doppelte pings/pongs. Thread.yield() essenitally bedeutet "ich bin fertig mit dem Prozessor, einige andere thread zu arbeiten". Das ist natürlich system-thread-scheduler-Implementierung auf meinem OS.
So, die Synchronisierung ordnungsgemäß müssen wir entfernen Zeile
so, dass threads konnte, synchronisieren Sie immer auf den gleichen Wert - nicht wirklich 🙂 Wie in der tolle Antwort oben gegeben - Strings (immutable Objekte) sind gefährlich, weil die sperren - denn wenn Sie erstellen String "A" auf 100 stellen, die in Ihrem Programm alle 100 Referenzen(Variablen) werden auf der gleichen "A" im Speicher - so könnte man oversynchronize.
Also eine Antwort auf Ihre ursprüngliche Frage, ändern Sie Ihren code wie folgt:
sowie-parallel dazu-PingPong Beispiel werden 100% korrekt umgesetzt (siehe EDIT^2).
Der obige code ist äquivalent zu:
Den PingPongThread.class ist ein Klasse-Objekt, z.B. auf jede Instanz, die Sie anrufen können, getClass() die hat immer nur eine Instanz.
Auch Sie könnte wie dieser
Auch, Lesen und Programm-Beispiele(läuft mehrere Male, wenn nötig) diese tutorial.
EDIT:
Werden technisch korrekt:
Die synchronisierte Methode ist die gleiche wie synchronized-Anweisung, sperren auf. Nennen wir das argument des synchronized-Anweisung "lock", so Marko wies darauf hin, "lock" ist eine variable und speichern eine Referenz auf ein Objekt/Instanz einer Klasse. Zitieren die Spezifikation:
Also die synchronizaton ist nicht konkret auf Wert - das Objekt/Instanz der Klasse, sondern auf die Objekt monitor verbunden mit dieser Instanz/Wert. Da
bleibt der Effekt der gleiche.
EDIT^2:
Folgenden auf die Kommentare Bemerkungen:
"und die parallel PingPong Beispiel wird 100% richtig umgesetzt" - was bedeutet, dass das gewünschte Verhalten erreicht ist (ohne Fehler).
IMHO, eine Lösung ist korrekt, wenn das Ergebnis korrekt ist. Es gibt viele Möglichkeiten der Lösung des Problems, so sind die nächsten Kriterien sind die Einfachheit/die Eleganz der Lösung - der phaser-Lösung ist der bessere Ansatz, denn wie Marko schon sagte in anderen Worten in einigen Kommentar-es gibt viel weniger chance, Fehler mit phaser-Objekt als die Verwendung von synchronized-Mechanismus - der kann gesehen werden von allen (nicht -) Lösung-Varianten in diesem Beitrag. Bemerkenswertes zu sehen ist auch der Vergleich von code-Größe und die Allgemeine Klarheit.
Den Schluss zu, diese Art der Konstrukte sollte immer verwendet werden, wenn Sie anwendbar sind-für das problem in Frage.
np, Sie sind herzlich willkommen 🙂
Kummo empfehle ich die Untersuchung der Phaser Beispiel - es ist eine sehr elegante Lösung. API-doc hier
Diese Aussage verwirrt mich: "Die synchronisierten block sperren nach Wert der Variablen und nicht die Referenz auf diesen Wert." Aber der Wert einer Variablen wird eine Referenz und in der Tat, jede einzelne Referenz-Wert ist mit einem einzigartigen monitor (der das einzigartige Objekt, das besonderen Wert bezieht sich auf).
Außerdem würde ich wirklich gerne anrufen, eine busy-waiting-Lösung "richtig". Auch wenn es richtig ist, im engeren technischen Sinn, nannte es, ohne Qualifikation ist nicht gerade lehrreich.
InformationsquelleAutor linski
Abschließend zu meiner Diskussion mit Brian Agnew, ich behaupte, dass dieser code verwendet
java.util.concurrent.Phaser
zu koordinieren, Ihre ping-pong-threads:Der wesentliche Unterschied zwischen dieser Lösung und die, die Sie versucht, code auf Ihre Lösung beschäftigt-kontrolliert eine Flagge, damit verschwenden CPU-Zeit (und Energie!). Der richtige Ansatz ist die Verwendung von blocking-Methoden, die einen thread schlafen, bis es Kenntnis von dem relevanten Ereignis.
InformationsquelleAutor Marko Topolnik
Jede Instanz des
PingPongThread
ist die Synchronisierung auf sich selbst und nicht auf eine freigegebene Ressource. Um zu Steuern, die Nachricht, die Sie brauchen werden, um zu synchronisieren, die auf eine gemeinsame Ressource (z.B.turn
variable ?)Jedoch glaube ich nicht, dass das wirklich funktionieren wird. Ich denke, Sie sollten check out
wait()
undnotify()
um dies zu tun (wenn man verstehen will, der threading-primitive). Sehen diese für ein Beispiel.java.util.concurrent
synchronizer, wiePhaser
oderCountDownLatch
.Ich war, mich das zu Fragen. Wenn das Ziel ist, zu verstehen, die threading primitiven, dann stehe ich von den oben genannten. Allerdings würde ich durchaus befürworten, Ihren Ansatz für die 'Produktion' type code!
Neue Java-Nutzer sollten nicht ausgesetzt werden, um
wait
undnotify
, außer unter der Kategorie "erweiterte Programmierung". Diese sehr rohen Mechanismus führt entweder falsche Verwendung oder viel boilerplate, um es zu implementieren rechts. Dass gerade Umwege entscheiden die Lernenden.Hinweis: die OP ' s Kommentar zu versuchen, zu verstehen, thread Grundlagen. Aber mein anderer Gedanke war, gehen in die völlig entgegengesetzte Richtung und deuten auf einen Akteur-framework (z.B. Akka). Nachdem alle, das ist die perfekte Abstraktion hier
Ich habe gerade das Gefühl, dass die Grundlagen nicht immer bedeuten ", wie es implementiert ist unten", ansonsten sollte jeder anfangen, Java zu lernen, indem Sie zuerst lernen, CPU-Architektur, assembler und C. ich Stimme mit Ihren Punkt mit Akka (mein Beispiel in diese Richtung wäre die Executor-Framework). Diese Werkzeuge abstrahieren, das ganze Konzept des threads, aber Sie bekommen den job zu erledigen der richtige Weg. Synchronisatoren sind der "Mittelweg" hier, das sind im Grunde nur Kapseln die richtige wait-notify-Idiome in eine narrensichere Paket.
InformationsquelleAutor Brian Agnew
Meine Lösung ist diese :
InformationsquelleAutor DomenicoC
Einer option ist die Verwendung SynchronousQueue .
InformationsquelleAutor Priyesh Jaiswal
Hier ist ein Ping-Pong Programm in Java geschrieben. Ping und Pong sind separate threads. Jeder thread ist sowohl für Verbraucher und Produzent. Bei jedem thread läuft es zwei Dinge tut
Den code basiert auf Oracles ProducerConsumerExample. Beachten Sie, dass die Ping-und Pong-Klassen sind fast identisch in Ihren code und in Ihrem Verhalten.
Die Fäden in der OP-code verwendet nur die "gegenseitigen Ausschluss" Teil der Objekte überwachen (als Brian Agnew oben vorgeschlagen). Sie nie aufrufen, eine warten. Damit Sie nur ausschließen eine andere, aber nie aufrufen, die java-Laufzeit, damit die anderen thread ausgeführt werden.
InformationsquelleAutor drlolly