Eine Rennbedingung ist eine besondere Bedingung, die innerhalb eines kritischen Abschnitts auftreten kann. Ein kritischer Abschnitt ist ein Codeabschnitt, der von mehreren Threads ausgeführt wird, und die Reihenfolge der Thread-Ausführung beeinflusst die Ergebnisse der gleichzeitigen Ausführung des kritischen Abschnitts.
Wenn mehrere Threads einen kritischen Abschnitt ausführen, kann das Ergebnis je nach Reihenfolge der Thread-Ausführung unterschiedlich sein. Dieser kritische Abschnitt enthält eine Race-Bedingung. Der Begriff für diese Wettlaufbedingung leitet sich von der Metapher ab, dass der Thread durch den kritischen Abschnitt rast und das Ergebnis dieses Wettlaufs das Ergebnis der Ausführung des kritischen Abschnitts beeinflusst.
Das klingt vielleicht etwas kompliziert, daher werde ich in den folgenden Abschnitten näher auf die Rennbedingungen und kritische Abschnitte eingehen.
Kritische Abschnitte
Das Ausführen von mehr als einem Thread innerhalb derselben Anwendung verursacht selbst keine Probleme . Probleme treten auf, wenn mehrere Threads auf dieselbe Ressource zugreifen. Zum Beispiel derselbe Speicher (Variable, Array oder Objekt), dasselbe System (Datenbank, Webdienst) oder dieselbe Datei.
Tatsächlich können Probleme auftreten, wenn ein oder mehrere Threads auf diese Ressourcen schreiben. Es ist sicher, dass mehrere Threads dieselbe Ressource lesen, solange sich die Ressource nicht ändert.
Hier ist ein Beispiel, das möglicherweise fehlschlägt, wenn mehrere Threads gleichzeitig ausgeführt werden:
public class Counter { protected long count = 0; public void add(long value){ this.count = this.count + value; } }
Stellen Sie sich vor, die Threads A und B würden ausgeführt. Führen Sie das aus Add-Methode der Instanz derselben Counter-Klasse. Es gibt keine Möglichkeit zu wissen, wann das Betriebssystem zwischen Threads wechselt. Der Code in der Add-Methode wird von der Java Virtual Machine nicht als separate atomare Anweisung ausgeführt. Stattdessen wird es als eine Reihe kleinerer Befehlssätze ausgeführt, ähnlich wie folgt:
Lesen Sie den this.count-Wert aus dem Speicher in das Register.
Wert zum Register hinzufügen.
Schreiben Sie den Wert im Register zurück in den Speicher.
Beobachten Sie, was passiert, wenn Threads A und B gemischt ausgeführt werden:
this.count = 0; A: Reads this.count into a register (0) B: Reads this.count into a register (0) B: Adds value 2 to register B: Writes register value (2) back to memory. this.count now equals 2 A: Adds value 3 to register A: Writes register value (3) back to memory. this.count now equals 3
Diese beiden Threads möchten 2 und 3 addieren zur Theke. Daher sollte der Wert nach der Ausführung dieser beiden Threads 5 sein. Da die beiden Threads jedoch in ihrer Ausführung verschachtelt sind, fallen die Ergebnisse am Ende unterschiedlich aus.
Im oben erwähnten Ausführungssequenzbeispiel lesen beide Threads den Wert 0 aus dem Speicher. Dann addieren sie ihre jeweiligen Werte 2 und 3 zu diesem Wert und schreiben das Ergebnis zurück in den Speicher. Anstelle von 5 ist der in this.count verbleibende Wert der Wert, den der letzte Thread dorthin geschrieben hat. Im obigen Beispiel ist es Thread A, es könnte aber auch Thread B sein.
Rennbedingungen in kritischen Abschnitten
Im obigen Beispiel enthält der Code für die Add-Methode einen kritischen Abschnitt. Wenn mehrere Threads diesen kritischen Abschnitt ausführen, tritt eine Race-Bedingung auf.
Formeller wird diese Situation, in der zwei Threads um dieselbe Ressource konkurrieren und die Reihenfolge, in der auf die Ressourcen zugegriffen wird, wichtig ist, als Race Condition bezeichnet. Ein Codeabschnitt, der eine Racebedingung verursacht, wird als kritischer Abschnitt bezeichnet.
Race Conditions verhindern
Um das Auftreten von Race Conditions zu verhindern, müssen Sie sicherstellen, dass der kritische Abschnitt, der ausgeführt wird, als atomare Anweisung ausgeführt wird. Das bedeutet, dass, sobald ein einzelner Thread ihn ausführt, andere Threads ihn nicht ausführen können, bis der erste Thread den kritischen Abschnitt verlassen hat.
Race Conditions können durch die Verwendung der Thread-Synchronisierung in kritischen Abschnitten vermieden werden. Die Thread-Synchronisierung kann mithilfe einer Synchronisierungssperre im Java-Code erreicht werden. Die Thread-Synchronisierung kann auch mithilfe anderer Synchronisierungskonzepte erreicht werden, beispielsweise durch Sperren oder atomare Variablen wie java.util.concurrent.atomic.AtomicInteger.
Durchsatz kritischer Abschnitte
Für kleinere kritische Abschnitte, sodass eine Synchronisationssperre für den gesamten kritischen Abschnitt funktionieren kann. Bei größeren kritischen Abschnitten ist es jedoch sinnvoller, sie in kleinere kritische Abschnitte zu unterteilen, sodass mehrere Threads jeden kleineren kritischen Abschnitt ausführen können. Es ist möglich, die Konkurrenz um gemeinsam genutzte Ressourcen zu reduzieren und den Durchsatz des gesamten kritischen Abschnitts zu erhöhen.
Hier ist ein sehr einfaches Java-Beispiel:
public class TwoSums { private int sum1 = 0; private int sum2 = 0; public void add(int val1, int val2){ synchronized(this){ this.sum1 += val1; this.sum2 += val2; } } }
Beachten Sie, wie die Add-Methode Werte zu den beiden Summenvariablen hinzufügt. Um Race Conditions zu verhindern, verfügt die intern durchgeführte Summierung über eine Java-Synchronisationssperre. Mit dieser Implementierung kann jeweils nur ein Thread diese Summierung durchführen.
Da die beiden Summenvariablen jedoch unabhängig voneinander sind, können Sie sie wie folgt in zwei separate Synchronisationssperren aufteilen:
public class TwoSums { private int sum1 = 0; private int sum2 = 0; public void add(int val1, int val2){ synchronized(this){ this.sum1 += val1; } synchronized(this){ this.sum2 += val2; } } }
Beachten Sie, dass zwei Threads diese Add-Methode gleichzeitig ausführen können. Ein Thread erwirbt die erste Synchronisationssperre und ein anderer Thread erwirbt die zweite Synchronisationssperre. Auf diese Weise warten Threads weniger Zeit untereinander.
Natürlich ist dieses Beispiel sehr einfach. Im wirklichen Leben kann die Trennung gemeinsam genutzter Ressourcen in kritischen Abschnitten komplexer sein und eine umfassendere Analyse der Möglichkeiten der Ausführungsreihenfolge erfordern.
Das Obige ist der Inhalt der Java-Rennbedingungen und kritischen Abschnitte. Weitere verwandte Inhalte finden Sie auf der chinesischen PHP-Website (www.php.cn)!