Table of Contents
ToggleReentrantLock in Java with Examples
ReentrantLock in Java 可重入的互斥鎖,又被稱為獨占鎖,實現了 Lock 介面,擁有與 synchronized 相同的同步加鎖功能,添加了類似鎖投票、定時鎖和可中斷鎖的一些特性, Reentrant Lock 同一個時間點,只能被一個執行緒持有,而可重入的意思是,可以被單個執行緒多次讀取,Java Reentrant Lock 本篇增加了範例,並透過單元測試來驗證產出結果。
檔案目錄
./
+- src
+- test
| +- org
| +- ruoxue
| +- java_147
| +- synchronization
| +- reentrantlock
| +- ReentrantLockWithExamplesTest.java
單元測試
Java Reentrant Lock 提供加鎖、解鎖等操作。
nolock
Java Reentrant Lock 建立 500 個執行緒,不加鎖對計數器加 1 ,等待 1 秒後結束任務,最終結果會不如預期。
protected class NoLockWorker implements Runnable {
private int count;
public NoLockWorker() {
}
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(1);
count++;
} catch (InterruptedException ex) {
ex.printStackTrace();
}
System.out.println(String.format("T[%d] count: %d", Thread.currentThread().getId(), count));
}
public int getCount() {
return count;
}
}
@Test
public void nolock() {
int expected = 500;
int taskSize = 500;
NoLockWorker worker = new NoLockWorker();
List<Thread> threads = Stream.generate(() -> new Thread(worker)).limit(taskSize).collect(Collectors.toList());
threads.forEach(e -> e.start());
threads.forEach(e -> {
try {
e.join();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
});
int count = worker.getCount();
System.out.println(count);
assertEquals(expected, count);
}
T[358] count: 496
T[401] count: 496
T[341] count: 496
T[366] count: 496
T[376] count: 496
496
lock
Java Reentrant Lock 建立 500 個執行緒,加鎖對計數器加 1 ,然後結束任務。
protected class Worker implements Runnable {
private final Lock lock = new ReentrantLock();
private int count;
public Worker() {
}
@Override
public void run() {
lock.lock();
try {
System.out.println(String.format("T[%d] lock acquired", Thread.currentThread().getId()));
//TimeUnit.SECONDS.sleep(1);
count++;
System.out.println(String.format("T[%d] count: %d", Thread.currentThread().getId(), count));
} catch (Exception ex) {
ex.printStackTrace();
} finally {
lock.unlock();
System.out.println(String.format("T[%d] lock released", Thread.currentThread().getId()));
}
}
public int getCount() {
return count;
}
}
@Test
public void lock() {
int expected = 500;
int taskSize = 500;
Worker worker = new Worker();
List<Thread> threads = Stream.generate(() -> new Thread(worker)).limit(taskSize).collect(Collectors.toList());
threads.forEach(e -> e.start());
threads.forEach(e -> {
try {
e.join();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
});
int count = worker.getCount();
System.out.println(count);
assertEquals(expected, count);
}
[497] count: 496
T[497] lock released
T[498] lock acquired
T[498] count: 497
T[498] lock released
T[504] lock acquired
T[504] count: 498
T[504] lock released
T[466] lock acquired
T[466] count: 499
T[466] lock released
T[507] lock acquired
T[507] count: 500
T[507] lock released
500
lockInterruptibly
Java Reentrant Lock 建立 3 個執行緒,加中斷鎖對計數器加 1 ,等待 1 秒後結束任務,中斷第 2 條執行緒,會拋出例外。
protected class LockInterruptiblyWorker implements Runnable {
private final Lock lock = new ReentrantLock();
private int count;
public LockInterruptiblyWorker() {
}
@Override
public void run() {
try {
lock.lockInterruptibly();
try {
System.out.println(String.format("T[%d] lock acquired", Thread.currentThread().getId()));
count++;
TimeUnit.SECONDS.sleep(1);
System.out.println(String.format("T[%d] count: %d", Thread.currentThread().getId(), count));
} catch (Exception ex) {
ex.printStackTrace();
} finally {
lock.unlock();
System.out.println(String.format("T[%d] lock released", Thread.currentThread().getId()));
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
public int getCount() {
return count;
}
}
@Test
public void lockInterruptibly() {
int expected = 2;
int taskSize = 3;
LockInterruptiblyWorker worker = new LockInterruptiblyWorker();
List<Thread> threads = Stream.generate(() -> new Thread(worker)).limit(taskSize).collect(Collectors.toList());
threads.forEach(e -> e.start());
Thread thread = threads.get(1);
thread.interrupt();
threads.forEach(e -> {
try {
e.join();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
});
int count = worker.getCount();
System.out.println(count);
assertEquals(expected, count);
}
T[11] lock acquired
java.lang.InterruptedException
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
at org.ruoxue.java_147.synchronization.ReentrantLockWithExamplesTest$LockInterruptiblyWorker.run(ReentrantLockWithExamplesTest.java:119)
at java.lang.Thread.run(Thread.java:750)
T[11] count: 1
T[11] lock released
T[13] lock acquired
T[13] count: 2
T[13] lock released
2
tryLock
ReentrantLock in Java 建立 3 個執行緒,加等候鎖對計數器加 1 ,等待 1 秒後結束任務,有兩個執行緒等候逾時,無執行任務。
protected class TryLockWorker implements Runnable {
private final Lock lock = new ReentrantLock();
private int count;
public TryLockWorker() {
}
@Override
public void run() {
try {
boolean isLockAcquired = lock.tryLock(100, TimeUnit.MILLISECONDS);
System.out.println(
String.format("T[%d] isLockAcquired: %b", Thread.currentThread().getId(), isLockAcquired));
if (isLockAcquired) {
try {
System.out.println(String.format("T[%d] lock acquired", Thread.currentThread().getId()));
count++;
TimeUnit.SECONDS.sleep(1);
System.out.println(String.format("T[%d] count: %d", Thread.currentThread().getId(), count));
} catch (Exception ex) {
ex.printStackTrace();
} finally {
lock.unlock();
System.out.println(String.format("T[%d] lock released", Thread.currentThread().getId()));
}
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
public int getCount() {
return count;
}
}
@Test
public void tryLock() {
int expected = 1;
int taskSize = 3;
TryLockWorker worker = new TryLockWorker();
List<Thread> threads = Stream.generate(() -> new Thread(worker)).limit(taskSize).collect(Collectors.toList());
threads.forEach(e -> e.start());
threads.forEach(e -> {
try {
e.join();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
});
int count = worker.getCount();
System.out.println(count);
assertEquals(expected, count);
}
T[12] isLockAcquired: true
T[12] lock acquired
T[11] isLockAcquired: false
T[13] isLockAcquired: false
T[12] count: 1
T[12] lock released
1
ReentrantLockWithExamplesTest.java
ReentrantLock in Java 新增單元測試,驗證 Reentrant Lock 是否符合預期。
package org.ruoxue.java_147.synchronization.reentrantlock;
import static org.junit.Assert.*;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.junit.Test;
public class ReentrantLockWithExamplesTest {
protected class NoLockWorker implements Runnable {
private int count;
public NoLockWorker() {
}
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(1);
count++;
} catch (InterruptedException ex) {
ex.printStackTrace();
}
System.out.println(String.format("T[%d] count: %d", Thread.currentThread().getId(), count));
}
public int getCount() {
return count;
}
}
@Test
public void nolock() {
int expected = 500;
int taskSize = 500;
NoLockWorker worker = new NoLockWorker();
List<Thread> threads = Stream.generate(() -> new Thread(worker)).limit(taskSize).collect(Collectors.toList());
threads.forEach(e -> e.start());
threads.forEach(e -> {
try {
e.join();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
});
int count = worker.getCount();
System.out.println(count);
assertEquals(expected, count);
}
protected class Worker implements Runnable {
private final Lock lock = new ReentrantLock();
private int count;
public Worker() {
}
@Override
public void run() {
lock.lock();
try {
System.out.println(String.format("T[%d] lock acquired", Thread.currentThread().getId()));
//TimeUnit.SECONDS.sleep(1);
count++;
System.out.println(String.format("T[%d] count: %d", Thread.currentThread().getId(), count));
} catch (Exception ex) {
ex.printStackTrace();
} finally {
lock.unlock();
System.out.println(String.format("T[%d] lock released", Thread.currentThread().getId()));
}
}
public int getCount() {
return count;
}
}
@Test
public void lock() {
int expected = 500;
int taskSize = 500;
Worker worker = new Worker();
List<Thread> threads = Stream.generate(() -> new Thread(worker)).limit(taskSize).collect(Collectors.toList());
threads.forEach(e -> e.start());
threads.forEach(e -> {
try {
e.join();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
});
int count = worker.getCount();
System.out.println(count);
assertEquals(expected, count);
}
protected class LockInterruptiblyWorker implements Runnable {
private final Lock lock = new ReentrantLock();
private int count;
public LockInterruptiblyWorker() {
}
@Override
public void run() {
try {
lock.lockInterruptibly();
try {
System.out.println(String.format("T[%d] lock acquired", Thread.currentThread().getId()));
TimeUnit.SECONDS.sleep(1);
count++;
System.out.println(String.format("T[%d] count: %d", Thread.currentThread().getId(), count));
} catch (Exception ex) {
ex.printStackTrace();
} finally {
lock.unlock();
System.out.println(String.format("T[%d] lock released", Thread.currentThread().getId()));
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
public int getCount() {
return count;
}
}
@Test
public void lockInterruptibly() {
int expected = 2;
int taskSize = 3;
LockInterruptiblyWorker worker = new LockInterruptiblyWorker();
List<Thread> threads = Stream.generate(() -> new Thread(worker)).limit(taskSize).collect(Collectors.toList());
threads.forEach(e -> e.start());
Thread thread = threads.get(1);
thread.interrupt();
threads.forEach(e -> {
try {
e.join();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
});
int count = worker.getCount();
System.out.println(count);
assertEquals(expected, count);
}
protected class TryLockWorker implements Runnable {
private final Lock lock = new ReentrantLock();
private int count;
public TryLockWorker() {
}
@Override
public void run() {
try {
boolean isLockAcquired = lock.tryLock(100, TimeUnit.MILLISECONDS);
System.out.println(
String.format("T[%d] isLockAcquired: %b", Thread.currentThread().getId(), isLockAcquired));
if (isLockAcquired) {
try {
System.out.println(String.format("T[%d] lock acquired", Thread.currentThread().getId()));
TimeUnit.SECONDS.sleep(1);
count++;
System.out.println(String.format("T[%d] count: %d", Thread.currentThread().getId(), count));
} catch (Exception ex) {
ex.printStackTrace();
} finally {
lock.unlock();
System.out.println(String.format("T[%d] lock released", Thread.currentThread().getId()));
}
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
public int getCount() {
return count;
}
}
@Test
public void tryLock() {
int expected = 1;
int taskSize = 3;
TryLockWorker worker = new TryLockWorker();
List<Thread> threads = Stream.generate(() -> new Thread(worker)).limit(taskSize).collect(Collectors.toList());
threads.forEach(e -> e.start());
threads.forEach(e -> {
try {
e.join();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
});
int count = worker.getCount();
System.out.println(count);
assertEquals(expected, count);
}
}
心得分享
Java ReentrantLock Examples 訪問共享資源時提供方法同步,操作共享資源的程式碼被鎖定和解鎖方法的調用所包圍,這會鎖定當前工作執行緒,並阻止所有其他試圖鎖定共享資源的執行緒, ReentrantLock in Java 提供了更多,更加全面的功能,具備更強的擴展性,相比 synchronized 而言, Reentrant Lock 比較不容易產生死鎖。