Seit Java 5 enthält das Paket java.util.concurrent.locks einige Sperrimplementierungen, sodass Sie Ihre eigenen Sperren nicht mehr implementieren müssen. Sie müssen jedoch immer noch verstehen, wie Sie diese Schlösser verwenden.
Ein einfaches Schloss
Beginnen wir mit einem Synchronisationsblock in Java:
öffentliche Klasse Zähler {private int count = 0; public int inc () {synchronized (this) {return ++ count; }}}Sie können sehen, dass es in der Inc () -Methode einen synchronisierten (diesen) Codeblock befindet. Dieser Codeblock kann sicherstellen, dass nur ein Thread gleichzeitig die Rückgabe ++ -Zahl ausführen kann. Obwohl der Code im synchronisierten Synchronisationsblock komplexer sein kann, reicht der einfache Betrieb der ++ Anzahl aus, um die Bedeutung der Threadsynchronisation auszudrücken.
Die folgende Zählerklasse verwendet Sperre anstelle von synchronisiert, um dasselbe Ziel zu erreichen:
öffentliche Klasse Zähler {private lock lock = new Lock (); private int count = 0; public int inc () {lock.lock (); int newCount = ++ count; lock.unlock (); NeueCount zurückgeben; }}Die Lock () -Methode sperrt das Lock -Instance -Objekt, sodass alle Threads, die die Lock () -Methode auf dem Objekt aufrufen, blockiert werden, bis die entsperr () -Methode des Sperrobjekts aufgerufen wird.
Hier ist eine einfache Implementierung der Schließklasse:
public class counter {public class lock {private boolean islocked = false; public synchronisierte void lock () löscht InterruptedException (while (islocked) {wait (); } islocked = true; } public synchronisierte void Unlock () {islocked = false; benachrichtigen(); }}Beachten Sie, dass die (islocked) Schleife im Inneren, die auch als "Spin -Lock" bezeichnet wird. Wenn islocked wahr ist, blockiert der Thread -Aufruf von Lock () und wartet auf dem Wait () -Anruf. Um zu verhindern, dass der Thread von Wait () zurückkehrt (), ohne den Notify () -Anruf (auch False Wakeup genannt) zu erhalten, wird der Thread den islockierten Zustand erneut überprüfen, um festzustellen, ob er sicher weiter ausgeführt werden kann oder wieder warten muss, anstatt zu glauben, dass der Thread nach dem Erwachen sicher weiter ausgeführt werden kann. Wenn islocked falsch ist, beendet der aktuelle Thread die while (islocked) Schleife und setze die Islocked auf True zurück, sodass andere Threads die Lock () -Methode auf die Sperresinstanz hinzufügen können.
Wenn der Thread den Code im kritischen Abschnitt (zwischen Lock () und Unlock ()) vervollständigt, wird entsperr () aufgerufen. Die Ausführung von Unlock () wird islockiert auf false und benachrichtigen (aufwachen).
Wiedereinzugbarkeit von Schlössern
Der synchronisierte Synchronisationsblock in Java ist wieder eingetreten. Dies bedeutet, dass, wenn ein Java -Thread in den synchronisierten Synchronisationsblock in den Code eingeht und somit eine Sperrung auf der Pipe erhält, die dem vom Synchronisationsblock verwendeten Synchronisationsobjekt entspricht, der Thread ein weiterer Java -Codeblock eingeben kann, der von demselben Pipeline -Objekt synchronisiert ist. Hier ist ein Beispiel:
public class Reentrant {public synchronisierte äußere () {inner (); } public synchronisierte innere () {// etwas tun}}Beachten Sie, dass sowohl äußere () als auch innere () synchronisiert sind, was synchronisierten (diese) Blöcken in Java entspricht. Wenn ein Thread in ober () aufruft, gibt es kein Problem mit innerer () in oter (), da beide Methoden (Codeblöcke) von demselben Verwaltungsobjekt ("This") synchronisiert werden. Wenn ein Faden bereits eine Sperre auf einem Rohrobjekt hat, hat er Zugriff auf alle vom Pipe -Objekt synchronisierten Codeblöcke. Das ist wieder eingetreten. Ein Thread kann einen beliebigen Codeblock eingeben, der von einem bereits entspannten Sperre synchronisiert ist.
Die oben angegebene Sperrimplementierung ist nicht wieder eingetragen. Wenn wir die Wiedereintrittsklasse wie die folgende umschreiben, wenn der Thread bei der inneren () -Methode lock.lock () der innere () -Methode aufruft.
public class Reentrant2 {lock lock = new Lock (); public outer () {lock.lock (); innere(); lock.unlock (); } public synchronisierte innere () {lock.lock (); // etwas lock.unlock (); }}Der Thread, der Outer () aufruft, sperrt zuerst die Sperre -Instanz und rufe dann weiter inner () an. In der inneren () -Methode versucht der Thread, die Sperre -Instanz erneut zu sperren, und die Aktion fällt aus (dh der Thread wird blockiert), da die Sperre -Instanz bereits in der äußeren () -Methode gesperrt ist.
Wenn Unlock () nicht zweimal zwischen lock () aufgerufen wird, wird der zweite Aufruf zur Sperrung blockiert. Nachdem Sie die Implementierung von Lock () gesehen haben, werden Sie feststellen, dass der Grund offensichtlich ist:
öffentliche Klasse Lock {boolean islocked = false; public synchronisierte void lock () löscht InterruptedException (while (islocked) {wait (); } islocked = true; } ...}Ob ein Gewinde die Lock () -Methode beenden darf, wird durch die Bedingungen in der while -Schleife (Spin -Lock) bestimmt. Die aktuelle Beurteilungsbedingung ist, dass die Sperrvorrichtung nur dann zulässig ist, wenn islocked falsch ist, ohne zu prüfen, welcher Faden sie sperrt.
Um diese Sperrklasse wieder einzubeziehen, müssen wir uns ein wenig ändern:
öffentliche Klasse Lock {boolean islocked = false; Faden gesperrt = null; int lockedCount = 0; public synchronisierte void lock () löscht InterruptedException {Thread CallingThread = thread.currentThread (); while (islocked && lockedby! = CallingThread) {wait (); } islocked = true; LockedCount ++; gesperrt von = Callthread; } public synchronisierte void Unlock () {if (thread.currentThread () == this.lockedby) {lockedCount--; if (lockedCount == 0) {islocked = false; benachrichtigen(); }}} ...}Beachten Sie, dass die aktuelle während der Schleife (Spin -Sperre) auch den Thread berücksichtigt, der die Sperreninstanz gesperrt hat. Wenn das aktuelle Sperrobjekt nicht gesperrt ist (isLocked = false) oder der aktuelle Aufruf -Thread die Sperre -Instanz gesperrt hat, kann die WHOP -Schleife nicht ausgeführt werden und der Thread -Aufruf von Lock () kann die Methode verlassen (Übersetzer Anmerkung: "Die Methode beenden" in der aktuellen Semantik bedeutet, dass es keine Waiten () und die Blockierung verursacht).
Darüber hinaus müssen wir die Anzahl der mit demselben Thread wiederholt ein Sperrobjekt aufzeichnen. Andernfalls entsperren Sie das gesamte Schloss, auch wenn das aktuelle Schloss mehrmals gesperrt wurde. Wir möchten nicht, dass das Schloss entsperrt wird, bis der Aufruf entsperr () die Anzahl der aufgerufene entsprechende Lock () -Arge erreicht.
Jetzt ist diese Schließklasse wieder eingetreten.
Die Fairness des Schlosses
Die synchronisierten Blöcke von Java garantieren nicht die Reihenfolge, in der Threads versuchen, sie einzugeben. Wenn also mehrere Threads weiterhin mit dem gleichen synchronisierten Synchronisationsblock zugreifen, besteht das Risiko, dass ein oder mehrere Threads niemals Zugriff erhalten - dh Zugriff ist immer anderen Threads zugeordnet. Diese Situation wird als Fadenhunger bezeichnet. Um dieses Problem zu vermeiden, müssen Schlösser Fairness erreichen. Die in diesem Artikel gezeigten Sperren werden intern mit synchronisierten Synchronisationsblöcken implementiert, sodass sie nicht garantiert fair sind.
Rufen Sie Unlock () in endgültiger Erklärung an
Wenn die Sperre zum Schutz des kritischen Bereichs verwendet wird und der kritische Bereich eine Ausnahme ausgeben kann, ist es sehr wichtig, Unlock () in der endgültigen Erklärung anzurufen. Dies stellt sicher, dass das Schlossobjekt entsperrt werden kann, damit andere Threads es weiterhin sperren können. Hier ist ein Beispiel:
lock.lock (); try {// kritische Abschnittscode durchführen, //, die eine Ausnahme auswerfen} endlich {lock.unlock ();}Diese einfache Struktur stellt sicher, dass das Schließobjekt entsperrt werden kann, wenn eine Ausnahme in den kritischen Bereich ausgelöst wird. Wenn Unlock () in der schließlich Anweisung nicht aufgerufen wird und eine Ausnahme in den kritischen Abschnitt ausgelöst wird, bleibt das Sperrobjekt für immer im gesperrten Zustand, was dazu führt, dass alle anderen Threads auf dem Sperrobjekt auf dem Blockieren von Lock () aufrufen.
Die oben genannten sind die Informationen über Java Multi-Threaded-Sperren. Wir werden in Zukunft weiterhin relevante Informationen hinzufügen. Vielen Dank für Ihre Unterstützung für diese Seite!