Java Lock Beispiel
Willkommen zum Java Lock Beispiel-Tutorial. Gewöhnlich verwenden wir in einer Multi-Thread-Umgebung das Schlüsselwort synchronized für die Thread-Sicherheit.
Java Lock
Meistens ist das Schlüsselwort synchronized der richtige Weg, aber es hat einige Mängel, die zur Einführung der Lock API im Java Concurrency-Paket geführt haben. Die Java 1.5 Concurrency API brachte das Paket java.util.concurrent.locks mit der Lock-Schnittstelle und einigen Implementierungsklassen heraus, um den Mechanismus zur Objektsperre zu verbessern. Einige wichtige Schnittstellen und Klassen in der Java Lock API sind:
- Lock: Dies ist die Basisschnittstelle für die Lock API. Sie bietet alle Funktionen des Schlüsselworts synchronized mit zusätzlichen Möglichkeiten, verschiedene Conditions für die Sperre zu erstellen, und bietet Timeout für den Thread, um auf die Sperre zu warten. Einige der wichtigen Methoden sind lock(), um die Sperre zu erlangen, unlock(), um die Sperre freizugeben, tryLock(), um für eine bestimmte Zeit auf die Sperre zu warten, newCondition(), um die Condition usw. zu erstellen.
- Condition: Condition-Objekte sind ähnlich dem Object wait-notify Modell mit zusätzlicher Funktion, verschiedene Sets von Wartebedingungen zu erstellen. Ein Condition-Objekt wird immer von einem Lock-Objekt erstellt. Einige der wichtigen Methoden sind await(), ähnlich wie wait(), und signal(), signalAll(), ähnlich wie notify() und notifyAll() Methoden.
- ReadWriteLock: Es enthält ein Paar zugehöriger Sperren, eine für nur-Lese-Operationen und eine andere für das Schreiben. Die Lesesperre kann gleichzeitig von mehreren Leser-Threads gehalten werden, solange keine Schreiber-Threads vorhanden sind. Die Schreibsperre ist exklusiv.
- ReentrantLock: Dies ist die am weitesten verbreitete Implementierungsklasse der Lock-Schnittstelle. Diese Klasse implementiert die Lock-Schnittstelle in ähnlicher Weise wie das Schlüsselwort synchronized. Neben der Implementierung der Lock-Schnittstelle enthält ReentrantLock einige Hilfsmethoden, um den Thread zu ermitteln, der die Sperre hält, Threads, die warten, um die Sperre zu erlangen usw. Synchronized-Blöcke sind von Natur aus reentrant, d.h., wenn ein Thread die Sperre am Monitor-Objekt hat und ein anderer synchronized-Block die Sperre am gleichen Monitor-Objekt benötigt, dann kann der Thread diesen Codeblock betreten. Ich denke, das ist der Grund für den Klassennamen ReentrantLock. Lassen Sie uns dieses Feature mit einem einfachen Beispiel verstehen.
public class Test{
public synchronized foo(){
//do something
bar();
}
public synchronized bar(){
//do some more
}
}
Wenn ein Thread foo() betritt, hat er die Sperre am Test-Objekt, also wenn er versucht, die bar() Methode auszuführen, wird dem Thread erlaubt, die bar() Methode auszuführen, da er bereits die Sperre am Test-Objekt hält, d.h. gleich wie synchronized(this).
Java Lock Beispiel – ReentrantLock in Java
Lassen Sie uns nun ein einfaches Beispiel sehen, in dem wir das Schlüsselwort synchronized durch die Java Lock API ersetzen. Nehmen wir an, wir haben eine Resource-Klasse mit einigen Operationen, bei denen wir wollen, dass sie thread-sicher ist, und einige Methoden, bei denen Thread-Sicherheit nicht erforderlich ist.
package com.journaldev.threads.lock;
public class Resource {
public void doSomething(){
//do some operation, DB read, write etc
}
public void doLogging(){
//logging, no need for thread safety
}
}
Lassen Sie uns nun sagen, dass wir eine Runnable-Klasse haben, in der wir Resource-Methoden 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 synchronized-Block verwende, um die Sperre am Resource-Objekt zu erlangen. Wir hätten ein Dummy-Objekt in der Klasse erstellen und dieses für den Sperrzweck verwenden können. Sehen wir nun, wie wir die Java Lock API verwenden und das obige Programm ohne das Schlüsselwort synchronized 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{
//release lock
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 die Sperre am Objekt nicht bekommt, loggt er einfach und verlässt. Ein weiterer wichtiger Punkt ist die Verwendung des try-finally-Blocks, um sicherzustellen, dass die Sperre freigegeben wird, auch wenn der Aufruf der Methode doSomething() eine Ausnahme wirft.
Java Lock vs synchronized
Basierend auf den obigen Details und dem Programm können wir leicht folgende Unterschiede zwischen Java Lock und Synchronisation feststellen.
- Die Java Lock API bietet mehr Sichtbarkeit und Optionen für die Sperrung, im Gegensatz zu synchronized, wo ein Thread möglicherweise unendlich lange auf die Sperre warten könnte, können wir tryLock() verwenden, um sicherzustellen, dass der Thread nur für eine bestimmte Zeit wartet.
- Der Synchronisationscode ist viel sauberer und leichter zu warten, während wir mit Lock gezwungen sind, einen try-finally-Block zu haben, um sicherzustellen, dass die Sperre freigegeben wird, auch wenn eine Ausnahme zwischen den Aufrufen der Methoden lock() und unlock() geworfen wird.
- Synchronisationsblöcke oder -methoden können nur eine Methode abdecken, während wir mit der Lock-API die Sperre in einer Methode erwerben 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 ein ReentrantLock-Objekt erstellen, so dass der am längsten wartende Thread zuerst die Sperre erhält.
- 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.