Lock ist eine Schnittstelle der obersten Ebene und alle ihre Methoden sind wie folgt:
Die Unterklassenliste lautet wie folgt:
Normalerweise verwenden wir ReentrantLock, um sie zu definieren Instanzen, Die Beziehung zwischen ihnen ist in der folgenden Abbildung dargestellt:
PS: Sync bedeutet Synchronisationssperre, FairSync bedeutet faire Sperre und NonfairSync bedeutet unfaire Sperre.
Das Erlernen einer Fertigkeit beginnt mit deren Verwendung, daher sind wir keine Ausnahme. Schauen wir uns zunächst die grundlegende Verwendung von ReentrantLock an:
public class LockExample { // 创建锁对象 private final ReentrantLock lock = new ReentrantLock(); public void method() { // 加锁操作 lock.lock(); try { // 业务代码...... } finally { // 释放锁 lock.unlock(); } } }
Nach dem Erstellen von ReentrantLock gibt es zwei wichtige Punkte:
Sperrvorgang: lock()
Sperrvorgang freigeben: unlock()
Viele Leute werden denken (besonders für unerfahrene Freunde), Die Standardimplementierung von ReentrantLock ist eine faire Sperre, dies ist jedoch standardmäßig nicht der Fall (dies liegt hauptsächlich an Leistungsaspekten). Die Ausführungsergebnisse des oben genannten Programms lauten wie folgt:
Anhand der obigen Ausführungsergebnisse können wir erkennen, dass ReentrantLock standardmäßig eine unfaire Sperre ist. Da die Namen der Threads entsprechend der Reihenfolge ihrer Erstellung erhöht werden, sollte die Ausführung der Threads in der Reihenfolge erhöht werden, wenn es sich um eine faire Sperre handelt Threads sind außer Betrieb. Diese Beschreibung ReentrantLock ist standardmäßig eine unfaire Sperre. Es ist auch sehr einfach, ReentrantLock als faire Sperre festzulegen. Sie müssen beim Erstellen von ReentrantLock nur einen echten Konstruktionsparameter festlegen:
import java.util.concurrent.locks.ReentrantLock; public class LockExample { // 创建锁对象 private static final ReentrantLock lock = new ReentrantLock(); public static void main(String[] args) { // 定义线程任务 Runnable runnable = new Runnable() { @Override public void run() { // 加锁 lock.lock(); try { // 打印执行线程的名字 System.out.println("线程:" + Thread.currentThread().getName()); } finally { // 释放锁 lock.unlock(); } } }; // 创建多个线程 for (int i = 0; i < 10; i++) { new Thread(runnable).start(); } } }Die Ausführungsergebnisse des obigen Programms sind wie folgt folgt:
Wie aus den obigen Ergebnissen ersichtlich ist, wird ReentrantLock zu einer fairen Sperre, wenn wir den wahren Konstruktionsparameter für ReentrantLock explizit festlegen, und die Reihenfolge, in der Threads Sperren erwerben, wird ordnungsgemäß.
Tatsächlich können wir anhand des Quellcodes von ReentrantLock auch erkennen, ob es sich um eine faire Sperre oder eine unfaire Sperre handelt. Ein Teil des Quellcodes von ReentrantLock ist wie folgt implementiert:
import java.util.concurrent.locks.ReentrantLock; public class LockExample { // 创建锁对象(公平锁) private static final ReentrantLock lock = new ReentrantLock(true); public static void main(String[] args) { // 定义线程任务 Runnable runnable = new Runnable() { @Override public void run() { // 加锁 lock.lock(); try { // 打印执行线程的名字 System.out.println("线程:" + Thread.currentThread().getName()); } finally { // 释放锁 lock.unlock(); } } }; // 创建多个线程 for (int i = 0; i < 10; i++) { new Thread(runnable).start(); } } }Wie aus dem ersichtlich ist Im obigen Quellcode erstellt ReentrantLock standardmäßig eine unfaire Sperre. Wenn der Wert des Konstruktionsparameters beim Erstellen explizit auf true gesetzt wird, wird eine faire Sperre erstellt.
2. Geben Sie die Sperre endlich frei.
Wenn Sie ReentrantLock verwenden, müssen Sie daran denken, die Sperre aufzuheben, da die Sperre sonst ständig belegt ist und andere Threads, die die Sperre verwenden, ewig warten. Daher verwenden wir ReentrantLock Achten Sie dabei unbedingt darauf, die Sperre endgültig zu lösen, damit Sie sicher sein können, dass die Sperre gelöst wird.
Gegenbeispiel
public ReentrantLock() { sync = new NonfairSync(); } public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
Das Ausführungsergebnis des obigen Programms lautet wie folgt:
Aus den obigen Ergebnissen ist ersichtlich, dass die Sperre beim Auftreten einer Ausnahme nicht normal aufgehoben wird führt dazu, dass sich andere Threads, die die Sperre verwenden, dauerhaft im Wartezustand befinden.
Positives Beispiel
import java.util.concurrent.locks.ReentrantLock; public class LockExample { // 创建锁对象 private static final ReentrantLock lock = new ReentrantLock(); public static void main(String[] args) { // 加锁操作 lock.lock(); System.out.println("Hello,ReentrantLock."); // 此处会报异常,导致锁不能正常释放 int number = 1 / 0; // 释放锁 lock.unlock(); System.out.println("锁释放成功!"); } }
Die Ausführungsergebnisse des obigen Programms lauten wie folgt:
Aus den obigen Ergebnissen ist ersichtlich, dass zwar eine Ausnahme in der Methode auftritt, diese jedoch keine Auswirkungen hat Geben Sie den Vorgang der ReentrantLock-Sperre frei, damit andere Threads, die diese Sperre verwenden, sie erhalten und normal ausführen können.
3. Die Sperre kann nicht mehrmals aufgehoben werdenDie Anzahl der Sperrvorgänge und die Anzahl der Entsperrvorgänge müssen eins zu eins übereinstimmen und eine Sperre kann nicht mehrmals aufgehoben werden, da dies dazu führt, dass das Programm einen Fehler meldet.
Gegenbeispiel
import java.util.concurrent.locks.ReentrantLock; public class LockExample { // 创建锁对象 private static final ReentrantLock lock = new ReentrantLock(); public static void main(String[] args) { // 加锁操作 lock.lock(); try { System.out.println("Hello,ReentrantLock."); // 此处会报异常 int number = 1 / 0; } finally { // 释放锁 lock.unlock(); System.out.println("锁释放成功!"); } } }
Die Ausführungsergebnisse des obigen Programms sind wie folgt :
Aus den obigen Ergebnissen können Sie erkennen, dass das Programm beim Ausführen der zweiten Entsperrung einen Fehler gemeldet und die Ausführung abgebrochen hat, was dazu führte, dass der Code nach der Ausnahme nicht normal ausgeführt wurde.
在使用 ReentrantLock 时,需要注意不要将加锁操作放在 try 代码中,这样会导致未加锁成功就执行了释放锁的操作,从而导致程序执行异常。
反例
import java.util.concurrent.locks.ReentrantLock; public class LockExample { // 创建锁对象 private static final ReentrantLock lock = new ReentrantLock(); public static void main(String[] args) { try { // 此处异常 int num = 1 / 0; // 加锁操作 lock.lock(); } finally { // 释放锁 lock.unlock(); System.out.println("锁释锁"); } System.out.println("程序执行完成."); } }
以上程序的执行结果如下:
从上述结果可以看出,如果将加锁操作放在 try 代码中,可能会导致两个问题:
未加锁成功就执行了释放锁的操作,从而导致了新的异常;
释放锁的异常会覆盖程序原有的异常,从而增加了排查问题的难度。
Das obige ist der detaillierte Inhalt vonWas sind die häufigsten Fallstricke von ReentrantLock in Java?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!