Willkommen zum Java-Lock-Beispiel-Tutorial. Normalerweise verwenden wir beim Arbeiten in einer multithreaded Umgebung synchronized für die Thread-Sicherheit.
Java Lock
Meistens ist das synchronisierte Schlüsselwort der richtige Weg, aber es hat einige Nachteile, die zur Aufnahme der Lock-API im Java Concurrency-Paket geführt haben. Die Java-1.5-Concurrency-API wurde mit dem
java.util.concurrent.locks
-Paket und dem Lock
-Interface sowie einigen Implementierungsklassen eingeführt, um den Objektsperrmechanismus zu verbessern. Einige wichtige Interfaces und Klassen in der Java Lock-API sind:
-
Lock: Dies ist das Basisschnittstellen für die Lock-API. Es bietet alle Funktionen des synchronisierten Schlüsselworts mit zusätzlichen Möglichkeiten zur Erstellung verschiedener Bedingungen für das Sperren, zur Bereitstellung eines Timeouts für den Thread, um auf das Sperren zu warten. Einige der wichtigen Methoden sind lock(), um das Sperren zu erwerben, unlock(), um das Sperren freizugeben, tryLock(), um für eine bestimmte Zeit auf das Sperren zu warten, newCondition(), um die Bedingung zu erstellen usw.
-
Bedingung: Objekte der Bedingung sind ähnlich dem Object wait-notify-Modell mit der zusätzlichen Funktion, verschiedene Sets von Wartevorgängen zu erstellen. Ein Bedingungsobjekt wird immer von einem Schlossobjekt erstellt. Einige der wichtigen Methoden sind await(), das ähnlich wie wait() ist, sowie signal() und signalAll(), die ähnlich wie die Methoden notify() und notifyAll() sind.
-
ReadWriteLock: Es enthält ein Paar zugehöriger Schlösser, eins für Lesevorgänge und ein weiteres für Schreibvorgänge. Das Lese-Schloss kann gleichzeitig von mehreren Lese-Threads gehalten werden, solange es keine Schreib-Threads gibt. Das Schreib-Schloss ist exklusiv.
-
ReentrantLock: Dies ist die am weitesten verbreitete Implementierungsklasse des Lock-Interfaces. Diese Klasse implementiert das Lock-Interface ähnlich wie das synchronisierte Schlüsselwort. Neben der Implementierung des Lock-Interfaces enthält ReentrantLock einige Hilfsmethoden, um den Thread zu erhalten, der das Schloss hält, Threads, die darauf warten, das Schloss zu erwerben, usw. Synchronisierte Blöcke sind rekursiv, d.h. wenn ein Thread das Schloss des Monitorobjekts hat und ein anderer synchronisierter Block das Schloss desselben Monitorobjekts benötigt, kann der Thread diesen Codeblock betreten. Ich denke, das ist der Grund für den Klassennamen ReentrantLock. Lassen Sie uns dieses Feature anhand eines einfachen Beispiels verstehen.
public class Test{ public synchronized foo(){ //etwas tun bar(); } public synchronized bar(){ //etwas mehr tun } }
Wenn ein Thread foo() betritt, hat er das Schloss auf dem Testobjekt, daher darf der Thread die Methode bar() ausführen, da er bereits das Schloss auf dem Testobjekt hält, d.h. das gleiche wie synchronized(this).
Java-Lock-Beispiel – ReentrantLock in Java
Jetzt sehen wir uns ein einfaches Beispiel an, in dem wir das synchronized-Schlüsselwort durch die Java Lock-API ersetzen. Angenommen, wir haben eine Ressourcenklasse mit einigen Operationen, bei denen wir möchten, dass sie threadsicher ist, und einigen Methoden, bei denen Threadsicherheit nicht erforderlich ist.
package com.journaldev.threads.lock;
public class Resource {
public void doSomething(){
//etwas tun, DB lesen, schreiben usw.
}
public void doLogging(){
//Protokollierung, keine Notwendigkeit für Threadsicherheit
}
}
Nun nehmen wir an, wir haben eine Klasse Runnable, in der wir Ressourcenmethoden verwenden werden.
package com.journaldev.threads.lock;
public class SynchronizedLockExample implements Runnable{
private Resource resource;
public SynchronizedLockExample(Resource r){
this.resource = r;
}
@Override
public void run() {
synchronized (resource) {
resource.doSomething();
}
resource.doLogging();
}
}
Beachten Sie, dass ich einen synchronisierten Block verwende, um den Lock auf das Ressourcenobjekt zu erwerben. Wir hätten ein Dummy-Objekt in der Klasse erstellen können und das für den Sperrzweck verwendet. Nun schauen wir uns an, wie wir die obige Methode ohne die Verwendung des synchronisierten Schlüsselworts mit der Java Lock API umschreiben können. Wir werden ReentrantLock in Java verwenden.
package com.journaldev.threads.lock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ConcurrencyLockExample implements Runnable{
private Resource resource;
private Lock lock;
public ConcurrencyLockExample(Resource r){
this.resource = r;
this.lock = new ReentrantLock();
}
@Override
public void run() {
try {
if(lock.tryLock(10, TimeUnit.SECONDS)){
resource.doSomething();
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
//Lock freigeben
lock.unlock();
}
resource.doLogging();
}
}
Wie Sie sehen können, verwende ich die Methode tryLock(), um sicherzustellen, dass mein Thread nur für eine bestimmte Zeit wartet und wenn er das Objekt nicht sperren kann, wird dies nur protokolliert und beendet. Ein weiterer wichtiger Punkt ist die Verwendung des try-finally-Blocks, um sicherzustellen, dass das Lock freigegeben wird, auch wenn der Aufruf der Methode doSomething() eine Ausnahme wirft.
Java Lock vs synchronized
Ausgehend von den obigen Details und dem Programm können wir folgende Unterschiede zwischen Java Lock und Synchronisation leicht feststellen.
- Die Java Lock API bietet mehr Sichtbarkeit und Optionen zur Sperrung, im Gegensatz zur Synchronisierung, bei der ein Thread möglicherweise unbegrenzt auf das Lock wartet. Wir können tryLock() verwenden, um sicherzustellen, dass der Thread nur für eine bestimmte Zeit wartet.
- Der Synchronisationscode ist viel sauberer und einfacher zu pflegen, während wir bei Lock gezwungen sind, einen try-finally-Block zu haben, um sicherzustellen, dass das Lock freigegeben wird, selbst wenn eine Ausnahme zwischen den Methodenaufrufen lock() und unlock() auftritt.
- Synchronisationsblöcke oder -methoden können nur eine Methode abdecken, während wir mit der Lock-API das Schloss in einer Methode erfassen und in einer anderen Methode freigeben können.
- Das Schlüsselwort „synchronized“ bietet keine Fairness, während wir die Fairness auf „true“ setzen können, während wir das ReentrantLock-Objekt erstellen, damit der am längsten wartende Thread das Schloss zuerst bekommt.
- Wir können verschiedene Bedingungen für Lock erstellen, und verschiedene Threads können auf verschiedene Bedingungen warten.
Das ist alles für das Java-Lock-Beispiel, ReentrantLock in Java und eine vergleichende Analyse mit dem Schlüsselwort „synchronized“.
Source:
https://www.digitalocean.com/community/tutorials/java-lock-example-reentrantlock