Lock und Condition
In Java basiert die Standard-Synchronisation auf dem Schlüsselwort synchronized und eingebauten Monitor-Objekten. In einigen Fällen reicht jedoch synchronized nicht aus, insbesondere wenn eine größere Flexibilität im Thread-Management erforderlich ist.
Allgemeine Beschreibung
Das Lock-Interface und das Condition-Interface, eingeführt im Paket java.util.concurrent.locks, bieten erweiterte Möglichkeiten für das Thread-Management.
In dieser Abbildung ist zu sehen, dass der erste Thread das Lock mit der Methode lock() erfasst und in diesem Moment kein anderer Thread dasselbe Lock erfassen kann. Sobald der gesamte Code innerhalb des Locks ausgeführt wurde, wird die Methode unlock() aufgerufen und das Lock freigegeben. Erst danach kann der zweite Thread das Lock erfassen.
Unterschied
Der Unterschied zwischen diesen beiden Interfaces besteht darin, dass die Implementierungen von Lock eine höherwertige Alternative zum synchronized-Block darstellen und die Implementierungen des Condition-Interfaces eine Alternative zu den Methoden notify()/wait() sind. Beide Interfaces sind Teil des Pakets java.util.concurrent.locks.
Praxisbeispiele
Stellen Sie sich vor, Sie verwalten eine Warteschlange für die Anmeldung zu einer Veranstaltung. Um Überfüllung zu vermeiden und eine ordnungsgemäße Sitzplatzvergabe zu gewährleisten, müssen Sie blockierende Mechanismen und Bedingungen einsetzen, damit neue Anmeldungen warten, bis ein freier Platz verfügbar ist.
ReentrantLock-Klasse
Die Klasse ReentrantLock aus dem Paket java.util.concurrent.locks ist eine Implementierung des Interfaces Lock. Sie bietet Funktionalität zur expliziten Verwaltung von Sperren.
Die wichtigsten Methoden von ReentrantLock:
lock(): Sperre erfassen;unlock(): Sperre freigeben;tryLock(): Versucht, die Sperre zu erfassen, und gibt true zurück, wenn dies erfolgreich ist;tryLock(long timeout, TimeUnit unit): Versucht, die Sperre für die angegebene Zeit zu erfassen;newCondition(): Erstellt eine Bedingung für die aktuelleLock.
Main.java
12345678910111213141516171819202122232425262728293031package com.example; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Main { // Creating a ReentrantLock object private final Lock lock = new ReentrantLock(); private int count = 0; // Method to increment the `count` variable public void increment() { lock.lock(); // Acquiring the `lock` try { count++; System.out.println("Count incremented to: " + count); } finally { lock.unlock(); // Releasing the `lock` } } public static void main(String[] args) { Main example = new Main(); // Runnable task to call the increment method Runnable task = example::increment; // Starting multiple threads to execute the task for (int i = 0; i < 5; i++) { new Thread(task).start(); } } }
Wie Sie sehen, verwenden wir im increment()-Methode das Sperren mit Lock. Wenn ein Thread die Methode betritt, erfasst er das Lock mit lock.lock(), führt dann den Code aus und gibt schließlich im finally-Block das Lock mit lock.unlock() wieder frei, sodass andere Threads eintreten können.
Wir geben das Lock im finally-Block aus einem bestimmten Grund frei: Dieser Block wird fast immer ausgeführt, selbst bei Ausnahmen, außer wenn das Programm beendet wird.
Condition-Interface
Wir können ein Condition-Objekt nur mit einer Bindung an eine bestimmte Lock-Implementierung erstellen. Aus diesem Grund wirken sich die Condition-Methoden nur auf die Sperrung dieser speziellen Lock-Implementierung aus.
Main.java
12private final Lock lock = new ReentrantLock(); private final Condition condition = lock.newCondition();
Die Hauptmethoden von Condition:
await(): Wartet auf ein Signal von einem anderen Thread;signal(): Hebt die Blockierung eines wartenden Threads auf einer Bedingung auf;signalAll(): Hebt die Blockierung von allen Threads auf, die auf die Bedingung warten.
Main.java
12345678910111213141516private final ReentrantLock lock = new ReentrantLock(); private final Condition condition = lock.newCondition(); private boolean ready = false; public void waitForCondition() throws InterruptedException { lock.lock(); // Acquire the `lock` try { while (!ready) { // Check if the condition is not met System.out.println("Waiting..."); // Print a waiting message condition.await(); // Wait for the condition to be signaled } System.out.println("Condition met!"); // Print a message when the condition is met } finally { lock.unlock(); // Release the `lock` } }
Die Methode waitForCondition() blockiert den Thread, bis die Variable ready true wird, was signalisiert, dass die Bedingung erfüllt ist. Sobald die Bedingung erfüllt ist, läuft der Thread weiter und zeigt die Meldung „Bedingung erfüllt!“ an.
Wenn die Methode await() aufgerufen wird, wird der Thread angehalten und gibt außerdem das von ihm gehaltene Lock frei. Wenn der Thread aufwacht, sollte er das Lock erneut erfassen und erst dann beginnt die Ausführung!
Codebeispiel
Nun folgt ein Beispiel für die Verwendung von ReentrantLock und Condition zur Verwaltung der Anmeldung zu einer Veranstaltung:
Ein kurzer Ausschnitt aus dem Video
Sperren mit ReentrantLock: Die Methode register() erfasst die Sperre mit lock.lock(), um zu verhindern, dass mehrere Threads gleichzeitig Code ausführen.
Bedingung mit Condition: Wenn keine freien Plätze verfügbar sind, ruft der Thread spaceAvailable.await() auf, um zu warten, bis wieder Platz verfügbar ist.
Freigabe der Sperre: Wenn ein Thread durch die Methode cancel() Platz freigegeben hat, ruft er spaceAvailable.signalAll() auf, um alle wartenden Threads zu benachrichtigen.
Ausnahmebehandlung: Die Verwendung von try-finally-Blöcken stellt sicher, dass das Lock auch bei Auftreten einer Ausnahme freigegeben wird.
Die Verwendung von Lock und Condition in Java ermöglicht eine flexiblere Steuerung von Threads und Synchronisation als der traditionelle synchronized-Mechanismus. Dies ist besonders in komplexen Szenarien nützlich, in denen eine präzisere Kontrolle über Threads und Wartebedingungen erforderlich ist.
Danke für Ihr Feedback!
Fragen Sie AI
Fragen Sie AI
Fragen Sie alles oder probieren Sie eine der vorgeschlagenen Fragen, um unser Gespräch zu beginnen
Can you explain the main differences between Lock and Condition in Java?
How does ReentrantLock improve thread management compared to synchronized blocks?
Can you provide a simple example of using Condition with ReentrantLock?
Awesome!
Completion rate improved to 3.33
Lock und Condition
Swipe um das Menü anzuzeigen
In Java basiert die Standard-Synchronisation auf dem Schlüsselwort synchronized und eingebauten Monitor-Objekten. In einigen Fällen reicht jedoch synchronized nicht aus, insbesondere wenn eine größere Flexibilität im Thread-Management erforderlich ist.
Allgemeine Beschreibung
Das Lock-Interface und das Condition-Interface, eingeführt im Paket java.util.concurrent.locks, bieten erweiterte Möglichkeiten für das Thread-Management.
In dieser Abbildung ist zu sehen, dass der erste Thread das Lock mit der Methode lock() erfasst und in diesem Moment kein anderer Thread dasselbe Lock erfassen kann. Sobald der gesamte Code innerhalb des Locks ausgeführt wurde, wird die Methode unlock() aufgerufen und das Lock freigegeben. Erst danach kann der zweite Thread das Lock erfassen.
Unterschied
Der Unterschied zwischen diesen beiden Interfaces besteht darin, dass die Implementierungen von Lock eine höherwertige Alternative zum synchronized-Block darstellen und die Implementierungen des Condition-Interfaces eine Alternative zu den Methoden notify()/wait() sind. Beide Interfaces sind Teil des Pakets java.util.concurrent.locks.
Praxisbeispiele
Stellen Sie sich vor, Sie verwalten eine Warteschlange für die Anmeldung zu einer Veranstaltung. Um Überfüllung zu vermeiden und eine ordnungsgemäße Sitzplatzvergabe zu gewährleisten, müssen Sie blockierende Mechanismen und Bedingungen einsetzen, damit neue Anmeldungen warten, bis ein freier Platz verfügbar ist.
ReentrantLock-Klasse
Die Klasse ReentrantLock aus dem Paket java.util.concurrent.locks ist eine Implementierung des Interfaces Lock. Sie bietet Funktionalität zur expliziten Verwaltung von Sperren.
Die wichtigsten Methoden von ReentrantLock:
lock(): Sperre erfassen;unlock(): Sperre freigeben;tryLock(): Versucht, die Sperre zu erfassen, und gibt true zurück, wenn dies erfolgreich ist;tryLock(long timeout, TimeUnit unit): Versucht, die Sperre für die angegebene Zeit zu erfassen;newCondition(): Erstellt eine Bedingung für die aktuelleLock.
Main.java
12345678910111213141516171819202122232425262728293031package com.example; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Main { // Creating a ReentrantLock object private final Lock lock = new ReentrantLock(); private int count = 0; // Method to increment the `count` variable public void increment() { lock.lock(); // Acquiring the `lock` try { count++; System.out.println("Count incremented to: " + count); } finally { lock.unlock(); // Releasing the `lock` } } public static void main(String[] args) { Main example = new Main(); // Runnable task to call the increment method Runnable task = example::increment; // Starting multiple threads to execute the task for (int i = 0; i < 5; i++) { new Thread(task).start(); } } }
Wie Sie sehen, verwenden wir im increment()-Methode das Sperren mit Lock. Wenn ein Thread die Methode betritt, erfasst er das Lock mit lock.lock(), führt dann den Code aus und gibt schließlich im finally-Block das Lock mit lock.unlock() wieder frei, sodass andere Threads eintreten können.
Wir geben das Lock im finally-Block aus einem bestimmten Grund frei: Dieser Block wird fast immer ausgeführt, selbst bei Ausnahmen, außer wenn das Programm beendet wird.
Condition-Interface
Wir können ein Condition-Objekt nur mit einer Bindung an eine bestimmte Lock-Implementierung erstellen. Aus diesem Grund wirken sich die Condition-Methoden nur auf die Sperrung dieser speziellen Lock-Implementierung aus.
Main.java
12private final Lock lock = new ReentrantLock(); private final Condition condition = lock.newCondition();
Die Hauptmethoden von Condition:
await(): Wartet auf ein Signal von einem anderen Thread;signal(): Hebt die Blockierung eines wartenden Threads auf einer Bedingung auf;signalAll(): Hebt die Blockierung von allen Threads auf, die auf die Bedingung warten.
Main.java
12345678910111213141516private final ReentrantLock lock = new ReentrantLock(); private final Condition condition = lock.newCondition(); private boolean ready = false; public void waitForCondition() throws InterruptedException { lock.lock(); // Acquire the `lock` try { while (!ready) { // Check if the condition is not met System.out.println("Waiting..."); // Print a waiting message condition.await(); // Wait for the condition to be signaled } System.out.println("Condition met!"); // Print a message when the condition is met } finally { lock.unlock(); // Release the `lock` } }
Die Methode waitForCondition() blockiert den Thread, bis die Variable ready true wird, was signalisiert, dass die Bedingung erfüllt ist. Sobald die Bedingung erfüllt ist, läuft der Thread weiter und zeigt die Meldung „Bedingung erfüllt!“ an.
Wenn die Methode await() aufgerufen wird, wird der Thread angehalten und gibt außerdem das von ihm gehaltene Lock frei. Wenn der Thread aufwacht, sollte er das Lock erneut erfassen und erst dann beginnt die Ausführung!
Codebeispiel
Nun folgt ein Beispiel für die Verwendung von ReentrantLock und Condition zur Verwaltung der Anmeldung zu einer Veranstaltung:
Ein kurzer Ausschnitt aus dem Video
Sperren mit ReentrantLock: Die Methode register() erfasst die Sperre mit lock.lock(), um zu verhindern, dass mehrere Threads gleichzeitig Code ausführen.
Bedingung mit Condition: Wenn keine freien Plätze verfügbar sind, ruft der Thread spaceAvailable.await() auf, um zu warten, bis wieder Platz verfügbar ist.
Freigabe der Sperre: Wenn ein Thread durch die Methode cancel() Platz freigegeben hat, ruft er spaceAvailable.signalAll() auf, um alle wartenden Threads zu benachrichtigen.
Ausnahmebehandlung: Die Verwendung von try-finally-Blöcken stellt sicher, dass das Lock auch bei Auftreten einer Ausnahme freigegeben wird.
Die Verwendung von Lock und Condition in Java ermöglicht eine flexiblere Steuerung von Threads und Synchronisation als der traditionelle synchronized-Mechanismus. Dies ist besonders in komplexen Szenarien nützlich, in denen eine präzisere Kontrolle über Threads und Wartebedingungen erforderlich ist.
Danke für Ihr Feedback!