【Java】深入理解ReentrantLock可重入鎖之簡單使用
學而不思則罔,思而不學則殆
【Java】深入理解ReentrantLock可重入鎖之簡單使用
ReentrantLock簡介
ReentrantLock主要利用CAS+AQS佇列來實現。它支援公平鎖和非公平鎖,兩者的實現類似。
CAS:Compare and Swap,比較並交換。CAS有3個運算元:記憶體值V、預期值A、要修改的新值B。當且僅當預期值A和記憶體值V相同時,將記憶體值V修改為B,否則什麼都不做。該操作是一個原子操作,被廣泛的應用在Java的底層實現中。在Java中,CAS主要是由sun.misc.Unsafe這個類通過JNI呼叫CPU底層指令實現
ReentrantLock方法總結
方法 | 說明 |
---|---|
lock | 等待執行緒獲取鎖,不能被中斷 |
lockInterruptibly | 等待執行緒獲取鎖,接收中斷訊號 |
tryLock | 方法是有返回值的,它表示用來嘗試獲取鎖,如果獲取成功,則返回true,如果獲取失敗(即鎖已被其他執行緒獲取),則返回false,這個方法無論如何都會立即返回 |
tryLock(long timeout, TimeUnit unit) | 嘗試獲取鎖,在指定時間內獲取成功會返回,超過時間返回false,可以被中斷 |
getHoldCount | 獲取當前執行緒持有鎖的次數 |
getQueueLength | 獲取等待鎖的執行緒個數 |
isFair | 鎖是否公平 |
hasQueuedThreads | 查詢是否有執行緒正在等待獲取此鎖 |
hasQueuedThread(Thread) | 查詢是指定執行緒是否正在等待獲取此鎖 |
isHeldByCurrentThread | 查詢當前執行緒是否持有此鎖 |
isLocked | 查詢此鎖是否由任何執行緒持有 |
newCondition | 建立Condition |
Condition方法總結
Condition可以定向喚醒執行緒
方法 | 說明 |
---|---|
await | 等待某個條件喚醒或者中斷 |
await (long,TimeUnit) | 等待xignal訊號或者中斷或者超時 |
awaitNanos | 等待signal訊號或者中斷或者超時 |
awaitUntil | 使當前執行緒等待,直到收到訊號或中斷,或指定的截止日期過期。 |
awaitUninterruptibly | 等待訊號,不能中斷 |
signal | 傳送一個訊號 |
signalAll | 傳送該條件的所有訊號 |
ReentrantLock簡單使用
常用方法測試
private static void testLock() {
sDiyThread1.start();
sDiyThread2.start();
sDiyThread3.start();
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
showMsg();
}
static ReentrantLock sReentrantLock = new ReentrantLock();
static DiyThread sDiyThread1 = new DiyThread(sReentrantLock);
static DiyThread sDiyThread2 = new DiyThread(sReentrantLock);
static DiyThread sDiyThread3 = new DiyThread(sReentrantLock);
static void showMsg() {
System.out.println(Thread.currentThread() + " 獲取當前執行緒持有鎖的次數:" + sReentrantLock.getHoldCount());
System.out.println(Thread.currentThread() + " 獲取等待鎖的執行緒個數:" + sReentrantLock.getQueueLength());
System.out.println(Thread.currentThread() + " 鎖是否公平:" + sReentrantLock.isFair());
System.out.println(Thread.currentThread() + " 查詢是否有執行緒正在等待獲取此鎖:" + sReentrantLock.hasQueuedThreads());
System.out.println(Thread.currentThread() + " 查詢是指定執行緒" + sDiyThread1.getName() + "正在等待獲取此鎖:" + sReentrantLock.hasQueuedThread(sDiyThread1));
System.out.println(Thread.currentThread() + " 查詢是指定執行緒" + sDiyThread2.getName() + "正在等待獲取此鎖:" + sReentrantLock.hasQueuedThread(sDiyThread2));
System.out.println(Thread.currentThread() + " 查詢是指定執行緒" + sDiyThread3.getName() + "正在等待獲取此鎖:" + sReentrantLock.hasQueuedThread(sDiyThread3));
System.out.println(Thread.currentThread() + " 查詢當前執行緒是否持有此鎖:" + sReentrantLock.isHeldByCurrentThread());
System.out.println(Thread.currentThread() + " 查詢此鎖是否由任何執行緒持有:" + sReentrantLock.isLocked());
}
static class DiyThread extends Thread {
ReentrantLock mReentrantLock;
public DiyThread(ReentrantLock reentrantLock) {
this.mReentrantLock = reentrantLock;
}
@Override
public void run() {
try {
//1.獲得鎖
mReentrantLock.lock();
mReentrantLock.lock();
TimeUnit.SECONDS.sleep(1);
showMsg();
//睡眠一分鐘
TimeUnit.MINUTES.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//2.釋放重入鎖
mReentrantLock.unlock();
mReentrantLock.unlock();
}
}
}
新建三個執行緒,其中一個執行緒獲取鎖後睡眠1分鐘,這個時候列印一下當前的鎖獲取情況資訊:
資訊如下:
Thread[Thread-0,5,main] 獲取當前執行緒持有鎖的次數:2
Thread[Thread-0,5,main] 獲取等待鎖的執行緒個數:2
Thread[Thread-0,5,main] 鎖是否公平:false
Thread[Thread-0,5,main] 查詢是否有執行緒正在等待獲取此鎖:true
Thread[Thread-0,5,main] 查詢是指定執行緒Thread-0正在等待獲取此鎖:false
Thread[Thread-0,5,main] 查詢是指定執行緒Thread-1正在等待獲取此鎖:true
Thread[Thread-0,5,main] 查詢是指定執行緒Thread-2正在等待獲取此鎖:true
Thread[Thread-0,5,main] 查詢當前執行緒是否持有此鎖:true
Thread[Thread-0,5,main] 查詢此鎖是否由任何執行緒持有:true
Thread[main,5,main] 獲取當前執行緒持有鎖的次數:0
Thread[main,5,main] 獲取等待鎖的執行緒個數:2
Thread[main,5,main] 鎖是否公平:false
Thread[main,5,main] 查詢是否有執行緒正在等待獲取此鎖:true
Thread[main,5,main] 查詢是指定執行緒Thread-0正在等待獲取此鎖:false
Thread[main,5,main] 查詢是指定執行緒Thread-1正在等待獲取此鎖:true
Thread[main,5,main] 查詢是指定執行緒Thread-2正在等待獲取此鎖:true
Thread[main,5,main] 查詢當前執行緒是否持有此鎖:false
Thread[main,5,main] 查詢此鎖是否由任何執行緒持有:true
測試tryLock
tryLock()方法是有返回值的,它表示用來嘗試獲取鎖,如果獲取成功,則返回true,如果獲取失敗(即鎖已被其他執行緒獲取),則返回false,這個方法無論如何都會立即返回。在拿不到鎖時不會一直在那等待。
static class TestTryLockThread extends Thread {
ReentrantLock mReentrantLock;
public TestTryLockThread(ReentrantLock reentrantLock) {
this.mReentrantLock = reentrantLock;
}
@Override
public void run() {
//1.嘗試獲得鎖
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " tryLock");
if (mReentrantLock.tryLock()) {
System.out.println(Thread.currentThread() + "獲取到鎖!");
} else {
System.out.println(Thread.currentThread() + "獲取不到鎖!");
return;
}
try {
boolean isLocked = mReentrantLock.isHeldByCurrentThread();
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " isLocked:" + isLocked);
if (isLocked) {
TimeUnit.MINUTES.sleep(1);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//2.釋放重入鎖
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " unlock");
mReentrantLock.unlock();
}
}
}
測試結果如下:
1603153131202 Thread[Thread-3,5,main] tryLock
Thread[Thread-3,5,main]獲取到鎖!
1603153131203 Thread[Thread-3,5,main] isLocked:true
1603153131203 Thread[Thread-4,5,main] tryLock
Thread[Thread-4,5,main]獲取不到鎖!
1603153131203 Thread[Thread-5,5,main] tryLock
Thread[Thread-5,5,main]獲取不到鎖!
1603153191204 Thread[Thread-3,5,main] unlock
執行緒【Thread-3】獲取到了鎖,然後休眠1分鐘,【Thread-4】【Thread-5】沒有獲取到鎖,但是呢,立馬就返回了。
測試tryLock(long timeout, TimeUnit unit)
這個方法去限定了一個嘗試獲取鎖的時間。
—獲取鎖成功則返回true;
—當失敗是分為兩種情況:
在引數範圍內,則不會立即返回值,會等待一段時間,這個時間就是傳入的具體引數值,在這個時間內獲取鎖成功,則依舊返回true;當過了引數範圍後,還是獲取鎖失敗,則立即返回false。
比如如下的測試demo:
//指定時間內是否獲取到鎖
static class TestTryLockTimeThread extends Thread {
ReentrantLock mReentrantLock;
int seconds;
public TestTryLockTimeThread(ReentrantLock reentrantLock, int seconds) {
this.mReentrantLock = reentrantLock;
this.seconds = seconds;
}
@Override
public void run() {
//1.嘗試獲得鎖
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " tryLock 嘗試獲取鎖的時間為:" + this.seconds);
try {
if (mReentrantLock.tryLock(seconds, TimeUnit.SECONDS)) {
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "獲取到鎖!");
try {
boolean isHeldByCurrentThread = mReentrantLock.isHeldByCurrentThread();
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " isHeldByCurrentThread:" + isHeldByCurrentThread);
if (isHeldByCurrentThread) {
TimeUnit.SECONDS.sleep(10); //拿到鎖後休眠10s
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//2.釋放重入鎖
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " unlock");
mReentrantLock.unlock();
}
} else {
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "獲取不到鎖!");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
private static void testTryLockLT() throws InterruptedException {
ReentrantLock reentrantLock = new ReentrantLock();
TestTryLockTimeThread timeThread1 = new TestTryLockTimeThread(reentrantLock, 0);
TestTryLockTimeThread timeThread2 = new TestTryLockTimeThread(reentrantLock, 5); //5秒內沒有拿到鎖就返回
TestTryLockTimeThread timeThread3 = new TestTryLockTimeThread(reentrantLock, 15);//15秒內沒有拿到鎖就返回
timeThread1.start();
TimeUnit.SECONDS.sleep(1); //休眠一秒,確保timeThread1拿到鎖
timeThread2.start();
timeThread3.start();
}
新建三個執行緒獲取重入鎖,【timeThread2】等待5秒還沒有獲取成功就返回,【timeThread3】等待15秒還沒有獲取成功就返回。而其中【timeThread1】會先獲取到鎖後休眠10s,那麼就會導致【timeThread2】獲取失敗,【timeThread3】獲取成功。log資訊如下:
1603154345942 Thread[Thread-3,5,main] tryLock 嘗試獲取鎖的時間為:0
1603154345943 Thread[Thread-3,5,main]獲取到鎖!
1603154345943 Thread[Thread-3,5,main] isHeldByCurrentThread:true
1603154346943 Thread[Thread-4,5,main] tryLock 嘗試獲取鎖的時間為:5
1603154346944 Thread[Thread-5,5,main] tryLock 嘗試獲取鎖的時間為:15
1603154351943 Thread[Thread-4,5,main]獲取不到鎖!
1603154355944 Thread[Thread-3,5,main] unlock
1603154355945 Thread[Thread-5,5,main]獲取到鎖!
1603154355945 Thread[Thread-5,5,main] isHeldByCurrentThread:true
1603154365946 Thread[Thread-5,5,main] unlock
可以看到【Thread-4】等待了5秒,在【46 - 51】的時候,就直接返回了。而【Thread-5】等待15秒,在【1603154355945】的是否獲取到了鎖。
測試lockInterruptibly
獲取鎖的時候可中斷
static class TestLockInterruptiblyThread extends Thread {
ReentrantLock mReentrantLock;
public TestLockInterruptiblyThread(ReentrantLock reentrantLock) {
this.mReentrantLock = reentrantLock;
}
@Override
public void run() {
try {
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " lockInterruptibly" );
//1.嘗試獲得鎖
mReentrantLock.lockInterruptibly();
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "獲取到鎖!");
//獲取鎖後休眠10s
TimeUnit.SECONDS.sleep(20);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (mReentrantLock.isHeldByCurrentThread()) {
//釋放鎖
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "釋放鎖!");
mReentrantLock.unlock();
}
}
}
}
private static void testLockInterruptibly() {
ReentrantLock reentrantLock = new ReentrantLock();
TestLockInterruptiblyThread thread1 = new TestLockInterruptiblyThread(reentrantLock);
TestLockInterruptiblyThread thread2 = new TestLockInterruptiblyThread(reentrantLock);
TestLockInterruptiblyThread thread3 = new TestLockInterruptiblyThread(reentrantLock);
thread1.setName("zy-test1");
thread2.setName("zy-test2");
thread3.setName("zy-test3");
thread1.start();
try {
//休眠5s確保thread1獲取鎖
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread2.start();
thread3.start();
try {
//休眠10s
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
//傳送中斷訊號
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " thread2 thread3 interrupt");
thread2.interrupt();
thread3.interrupt();
}
測試主要是新建三個執行緒:
第一個執行緒獲取鎖休眠20s
第二個執行緒和第三個執行緒5秒後啟動,確保第一個執行緒獲取鎖
在這10秒中通過jstack列印一下執行緒狀態資訊
啟動10秒後傳送中斷訊號,第二個和第三個執行緒
log資訊如下:
1603237475627 Thread[zy-test1,5,main] lockInterruptibly
1603237475627 Thread[zy-test1,5,main]獲取到鎖!
1603237480628 Thread[zy-test2,5,main] lockInterruptibly
1603237480628 Thread[zy-test3,5,main] lockInterruptibly
1603237490628 Thread[main,5,main] thread2 thread3 interrupt
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 com.lock.ReentrantLockTest$TestLockInterruptiblyThread.run(ReentrantLockTest.java:38)
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 com.lock.ReentrantLockTest$TestLockInterruptiblyThread.run(ReentrantLockTest.java:38)
1603237495627 Thread[zy-test1,5,main]釋放鎖!
可以看到執行緒【zy-test1】正常休眠結束釋放鎖,執行緒【zy-test2】【zy-test3】被中斷了但我們手動傳送中斷訊號的時候,列印出了中斷的異常堆疊。
其中線上程【zy-test2】【zy-test3】等待獲取鎖的執行緒狀態如下:
$ jstack.exe 6056
2020-10-21 07:44:48
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.181-b13 mixed mode):
"zy-test3" #16 prio=5 os_prio=0 tid=0x0000000022864000 nid=0x5e8 waiting on condition [0x000000002350e000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000007406ab408> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:897)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
at com.lock.ReentrantLockTest$TestLockInterruptiblyThread.run(ReentrantLockTest.java:38)
"zy-test2" #15 prio=5 os_prio=0 tid=0x0000000022860800 nid=0x964 waiting on condition [0x000000002340f000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000007406ab408> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:897)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
at com.lock.ReentrantLockTest$TestLockInterruptiblyThread.run(ReentrantLockTest.java:38)
"zy-test1" #14 prio=5 os_prio=0 tid=0x0000000022803000 nid=0x33d8 waiting on condition [0x000000002330e000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at java.lang.Thread.sleep(Thread.java:340)
at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
at com.lock.ReentrantLockTest$TestLockInterruptiblyThread.run(ReentrantLockTest.java:41)
此時三個執行緒的狀態分別是:
“zy-test1” TIMED_WAITING (sleeping)
“zy-test2” WAITING (parking)
“zy-test3” WAITING (parking)
知道此時三個執行緒都是出於掛起的狀態,但是掛起的方式不同,【zy-test1】是通過sleep方式,進入TIMED_WAITING 狀態,可以通過中斷訊號打斷或者時間到了自動喚醒。【zy-test2】和【zy-test3】則是通過Unsafe.park方法讓執行緒處於WAITING 狀態。
測試Condition
測試Condition.await()
static class TestConditionThread extends Thread {
ReentrantLock mReentrantLock;
Condition mCondition;
public TestConditionThread(ReentrantLock reentrantLock, Condition condition) {
this.mReentrantLock = reentrantLock;
this.mCondition = condition;
}
@Override
public void run() {
try {
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " lock");
//1.嘗試獲得鎖
mReentrantLock.lock();
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "獲取到鎖");
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "await start");
mCondition.await();
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "await end");
} catch (Exception e) {
e.printStackTrace();
} finally {
if (mReentrantLock.isHeldByCurrentThread()) {
//釋放鎖
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "釋放鎖");
mReentrantLock.unlock();
}
}
}
}
//簡單使用
private static void testNewCondition1() {
ReentrantLock reentrantLock = new ReentrantLock();
Condition condition = reentrantLock.newCondition();
TestConditionThread thread = new TestConditionThread(reentrantLock, condition);
thread.setName("test_condition_await");
thread.start();
}
新建一個執行緒,獲取鎖後呼叫await()進入等待狀態。執行緒狀態如下:
$ jstack.exe 1220
2020-10-21 08:19:52
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.181-b13 mixed mode):
"DestroyJavaVM" #15 prio=5 os_prio=0 tid=0x00000000025ce800 nid=0x1d20 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"test_condition_await" #14 prio=5 os_prio=0 tid=0x0000000021ef1800 nid=0x3eb4 waiting on condition [0x0000000022a6f000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000007406ad228> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
at com.lock.ReentrantLockTest$TestConditionThread.run(ReentrantLockTest.java:45)
通過中斷喚醒
TimeUnit.SECONDS.sleep(2);
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " interrupt");
thread.interrupt();
1603239799798 Thread[test_condition_await,5,main] lock
1603239799798 Thread[test_condition_await,5,main]獲取到鎖
1603239799798 Thread[test_condition_await,5,main]await start
1603239801799 Thread[main,5,main] interrupt
1603239801802 Thread[test_condition_await,5,main]釋放鎖
java.lang.InterruptedException
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.reportInterruptAfterWait(AbstractQueuedSynchronizer.java:2014)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2048)
at com.lock.ReentrantLockTest$TestConditionThread.run(ReentrantLockTest.java:47)
傳送中斷訊號後,執行緒被打斷,喚醒執行緒。
通過signal喚醒
新建一個執行緒用來測試傳送signal訊號
static class TestSignalThread extends Thread {
ReentrantLock mReentrantLock;
Condition mCondition;
public TestSignalThread(ReentrantLock reentrantLock, Condition condition) {
this.mReentrantLock = reentrantLock;
this.mCondition = condition;
}
@Override
public void run() {
try {
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " lock");
//1.嘗試獲得鎖
mReentrantLock.lock();
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "獲取到鎖");
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " 傳送signal訊號");
mCondition.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (mReentrantLock.isHeldByCurrentThread()) {
//釋放鎖
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "釋放鎖");
mReentrantLock.unlock();
}
}
}
}
TimeUnit.SECONDS.sleep(2);
TestSignalThread signalThread = new TestSignalThread(reentrantLock, condition);
signalThread.setName("test_signal");
signalThread.start();
測試結果如下:
1603240076885 Thread[test_condition_await,5,main] lock
1603240076886 Thread[test_condition_await,5,main]獲取到鎖
1603240076886 Thread[test_condition_await,5,main]await start
1603240078883 Thread[test_signal,5,main] lock
1603240078883 Thread[test_signal,5,main]獲取到鎖
1603240078883 Thread[test_signal,5,main] 傳送signal訊號
1603240078883 Thread[test_signal,5,main]釋放鎖
1603240078883 Thread[test_condition_await,5,main]await end
1603240078883 Thread[test_condition_await,5,main]釋放鎖
【test_signal】傳送訊號後且釋放鎖後,【test_condition_await】被喚醒。
通過signalAll喚醒
static class TestSignalThread extends Thread {
ReentrantLock mReentrantLock;
Condition mCondition;
public TestSignalThread(ReentrantLock reentrantLock, Condition condition) {
this.mReentrantLock = reentrantLock;
this.mCondition = condition;
}
@Override
public void run() {
try {
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " lock");
//1.嘗試獲得鎖
mReentrantLock.lock();
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "獲取到鎖");
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " 傳送signalAll訊號");
mCondition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (mReentrantLock.isHeldByCurrentThread()) {
//釋放鎖
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "釋放鎖");
mReentrantLock.unlock();
}
}
}
}
1603240230998 Thread[test_condition_await,5,main] lock
1603240230998 Thread[test_condition_await,5,main]獲取到鎖
1603240230998 Thread[test_condition_await,5,main]await start
1603240233002 Thread[test_signal,5,main] lock
1603240233002 Thread[test_signal,5,main]獲取到鎖
1603240233002 Thread[test_signal,5,main] 傳送signalAll訊號
1603240233002 Thread[test_signal,5,main]釋放鎖
1603240233003 Thread[test_condition_await,5,main]await end
1603240233003 Thread[test_condition_await,5,main]釋放鎖
測試await(long time, TimeUnit unit)
超時等待,超過時間沒有喚醒會自動結束
static class TestConditionThread extends Thread {
ReentrantLock mReentrantLock;
Condition mCondition;
public TestConditionThread(ReentrantLock reentrantLock, Condition condition) {
this.mReentrantLock = reentrantLock;
this.mCondition = condition;
}
@Override
public void run() {
try {
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " lock");
//1.嘗試獲得鎖
mReentrantLock.lock();
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "獲取到鎖");
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "await(10, TimeUnit.SECONDS) start");
mCondition.await(10, TimeUnit.SECONDS);
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "await(10, TimeUnit.SECONDS) end");
} catch (Exception e) {
e.printStackTrace();
} finally {
if (mReentrantLock.isHeldByCurrentThread()) {
//釋放鎖
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "釋放鎖");
mReentrantLock.unlock();
}
}
}
}
private static void testNewCondition1() throws InterruptedException {
ReentrantLock reentrantLock = new ReentrantLock();
Condition condition = reentrantLock.newCondition();
TestConditionThread thread = new TestConditionThread(reentrantLock, condition);
thread.setName("test_condition_await");
thread.start();
}
等待10秒後,沒有收到中斷訊號或者signal訊號也會自動喚醒:
1603241143625 Thread[test_condition_await,5,main]獲取到鎖
1603241143625 Thread[test_condition_await,5,main]await(10, TimeUnit.SECONDS) start
1603241153627 Thread[test_condition_await,5,main]await(10, TimeUnit.SECONDS) end
1603241153627 Thread[test_condition_await,5,main]釋放鎖
測試awaitNanos
static class TestConditionThread extends Thread {
ReentrantLock mReentrantLock;
Condition mCondition;
public TestConditionThread(ReentrantLock reentrantLock, Condition condition) {
this.mReentrantLock = reentrantLock;
this.mCondition = condition;
}
@Override
public void run() {
try {
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " lock");
//1.嘗試獲得鎖
mReentrantLock.lock();
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "獲取到鎖");
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "awaitNanos start");
long na = TimeUnit.NANOSECONDS.convert(5, TimeUnit.SECONDS);
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " na:"+na);
mCondition.awaitNanos(na);
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "awaitNanos end");
} catch (Exception e) {
e.printStackTrace();
} finally {
if (mReentrantLock.isHeldByCurrentThread()) {
//釋放鎖
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "釋放鎖");
mReentrantLock.unlock();
}
}
}
}
測試結果:
等待5秒後,沒有收到中斷訊號或者signal訊號也會自動喚醒:
1603241437769 Thread[test_condition_await,5,main] lock
1603241437770 Thread[test_condition_await,5,main]獲取到鎖
1603241437770 Thread[test_condition_await,5,main]awaitNanos start
1603241437770 Thread[test_condition_await,5,main] na:5000000000
1603241442771 Thread[test_condition_await,5,main]awaitNanos end
1603241442771 Thread[test_condition_await,5,main]釋放鎖
測試awaitUntil
使當前執行緒等待,直到收到訊號或中斷,或指定的截止日期過期。
static class TestConditionThread extends Thread {
ReentrantLock mReentrantLock;
Condition mCondition;
public TestConditionThread(ReentrantLock reentrantLock, Condition condition) {
this.mReentrantLock = reentrantLock;
this.mCondition = condition;
}
@Override
public void run() {
try {
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " lock");
//1.嘗試獲得鎖
mReentrantLock.lock();
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "獲取到鎖");
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "awaitUntil start");
mCondition.awaitUntil(new Date(System.currentTimeMillis() + 1000 * 5));
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "awaitUntil end");
} catch (Exception e) {
e.printStackTrace();
} finally {
if (mReentrantLock.isHeldByCurrentThread()) {
//釋放鎖
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "釋放鎖");
mReentrantLock.unlock();
}
}
}
}
測試結果,測試截止日期為當前時間+5秒:
1603283671821 Thread[test_condition_await,5,main] lock
1603283671821 Thread[test_condition_await,5,main]獲取到鎖
1603283671821 Thread[test_condition_await,5,main]awaitUntil start
1603283676832 Thread[test_condition_await,5,main]awaitUntil end
1603283676832 Thread[test_condition_await,5,main]釋放鎖
測試awaitUninterruptibly
導致當前執行緒等待,直到收到訊號。意思中斷不了
static class TestConditionThread extends Thread {
ReentrantLock mReentrantLock;
Condition mCondition;
public TestConditionThread(ReentrantLock reentrantLock, Condition condition) {
this.mReentrantLock = reentrantLock;
this.mCondition = condition;
}
@Override
public void run() {
try {
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " lock");
//1.嘗試獲得鎖
mReentrantLock.lock();
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "獲取到鎖");
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "awaitUninterruptibly start");
mCondition.awaitUninterruptibly();
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "awaitUninterruptibly end");
} catch (Exception e) {
e.printStackTrace();
} finally {
if (mReentrantLock.isHeldByCurrentThread()) {
//釋放鎖
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "釋放鎖");
mReentrantLock.unlock();
}
}
}
}
//測試傳送signal的執行緒
static class TestSignalThread extends Thread {
ReentrantLock mReentrantLock;
Condition mCondition;
public TestSignalThread(ReentrantLock reentrantLock, Condition condition) {
this.mReentrantLock = reentrantLock;
this.mCondition = condition;
}
@Override
public void run() {
try {
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " lock");
//1.嘗試獲得鎖
mReentrantLock.lock();
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "獲取到鎖後休眠2秒");
TimeUnit.SECONDS.sleep(2);
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " 傳送signalAll訊號");
mCondition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (mReentrantLock.isHeldByCurrentThread()) {
//釋放鎖
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "釋放鎖");
mReentrantLock.unlock();
}
}
}
}
private static void testNewCondition1() throws InterruptedException {
ReentrantLock reentrantLock = new ReentrantLock();
Condition condition = reentrantLock.newCondition();
TestConditionThread thread = new TestConditionThread(reentrantLock, condition);
thread.setName("test_condition_await");
thread.start();
TimeUnit.SECONDS.sleep(2);
System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " interrupt");
thread.interrupt();
TestSignalThread signalThread = new TestSignalThread(reentrantLock, condition);
signalThread.setName("test_signal");
signalThread.start();
}
1603284043331 Thread[test_condition_await,5,main] lock
1603284043331 Thread[test_condition_await,5,main]獲取到鎖
1603284043331 Thread[test_condition_await,5,main]awaitUninterruptibly start
1603284045332 Thread[main,5,main] interrupt
1603284045340 Thread[test_signal,5,main] lock
1603284045340 Thread[test_signal,5,main]獲取到鎖後休眠2秒
1603284047347 Thread[test_signal,5,main] 傳送signalAll訊號
1603284047347 Thread[test_signal,5,main]釋放鎖
1603284047347 Thread[test_condition_await,5,main]awaitUninterruptibly end
1603284047347 Thread[test_condition_await,5,main]釋放鎖
傳送中斷訊號沒有打斷【test_condition_await】執行緒的等待。只有傳送訊號才能喚醒【test_condition_await】執行緒。
簡單用法
測試三個執行緒順序列印1,2,3…到100 - 實現一
通過公共狀態類維護
//三個執行緒,順序執行,列印100
private static void test1() {
ReentrantLock reentrantLock = new ReentrantLock();
Bean bean = new Bean(100, new AtomicInteger(0));
TestOneRunnable testOneRunnable1 = new TestOneRunnable(reentrantLock, bean, 0);
TestOneRunnable testOneRunnable2 = new TestOneRunnable(reentrantLock, bean, 1);
TestOneRunnable testOneRunnable3 = new TestOneRunnable(reentrantLock, bean, 2);
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.submit(testOneRunnable1);
executorService.submit(testOneRunnable2);
executorService.submit(testOneRunnable3);
executorService.shutdown();
}
static class Bean {
int index;
volatile AtomicInteger status;
int num;
public Bean(int num, AtomicInteger status) {
this.num = num;
this.status = status;
}
}
static class TestOneRunnable implements Runnable {
ReentrantLock mReentrantLock;
Bean mBean;
int mFlag = -1;
public TestOneRunnable(ReentrantLock reentrantLock, Bean bean, int flag) {
this.mReentrantLock = reentrantLock;
this.mBean = bean;
this.mFlag = flag;
}
@Override
public void run() {
//System.out.println(Thread.currentThread() + " start");
while (mBean.index < mBean.num) {
try {
//1.獲得鎖
mReentrantLock.lock();
if (mBean.index >= mBean.num) {
return;
}
//改變狀態
if (mFlag == (mBean.status.intValue() % 3)) {
System.out.println(Thread.currentThread() + " " + mBean.index++);
mBean.status.incrementAndGet(); //狀態+1
}
} finally {
//2.釋放重入鎖
mReentrantLock.unlock();
}
}
//System.out.println(Thread.currentThread() + " end");
}
}
測試結果:
Thread[pool-1-thread-1,5,main] 0
Thread[pool-1-thread-2,5,main] 1
Thread[pool-1-thread-3,5,main] 2
Thread[pool-1-thread-1,5,main] 3
Thread[pool-1-thread-2,5,main] 4
Thread[pool-1-thread-3,5,main] 5
...
Thread[pool-1-thread-1,5,main] 93
Thread[pool-1-thread-2,5,main] 94
Thread[pool-1-thread-3,5,main] 95
Thread[pool-1-thread-1,5,main] 96
Thread[pool-1-thread-2,5,main] 97
Thread[pool-1-thread-3,5,main] 98
Thread[pool-1-thread-1,5,main] 99
三個執行緒迴圈列印數字到100,每個執行緒獲得鎖過後,判斷是否該自己列印,如果該自己列印,列印數字並改變陣列和狀態後,在釋放鎖,在嘗試獲取鎖,等待其他執行緒更改狀態滿足條件。
測試三個執行緒順序列印1,2,3…到100 - 實現二
通過Condition實現
static class TestPrintThread extends Thread {
ReentrantLock mReentrantLock;
PrintBean mPrintBean;
Condition mConditionCurr; //當前執行緒的條件
Condition mConditionNext; //下一個執行緒的條件
public TestPrintThread(ReentrantLock reentrantLock, PrintBean printBean, Condition conditionCurr, Condition conditionNext) {
this.mReentrantLock = reentrantLock;
this.mPrintBean = printBean;
this.mConditionCurr = conditionCurr;
this.mConditionNext = conditionNext;
}
@Override
public void run() {
//System.out.println(Thread.currentThread() + " start");
while (mPrintBean.currentIndex < mPrintBean.max) {
try {
//1.獲得鎖
mReentrantLock.lock();
if (mPrintBean.currentIndex >= mPrintBean.max) {
System.out.println(Thread.currentThread() + " end1");
mConditionNext.signal();
return;
}
System.out.println(Thread.currentThread() + " " + mPrintBean.currentIndex++);
//傳送訊號,喚醒下一個執行緒
mConditionNext.signal();
//當前執行緒等待
//System.out.println(Thread.currentThread() + " wait");
mConditionCurr.await();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//傳送訊號,喚醒下一個執行緒,防止下一次迴圈的時候直接退出,沒有傳送訊號
mConditionNext.signal();
//釋放重入鎖
mReentrantLock.unlock();
}
}
System.out.println(Thread.currentThread() + " end2");
}
}
//三個執行緒,順序執行,列印100
private static void test100() throws InterruptedException {
ReentrantLock reentrantLock = new ReentrantLock();
Condition condition1 = reentrantLock.newCondition();
Condition condition2 = reentrantLock.newCondition();
Condition condition3 = reentrantLock.newCondition();
PrintBean printBean = new PrintBean(100, 0);
TestPrintThread printRunnable1 = new TestPrintThread(reentrantLock, printBean, condition1, condition2);
TestPrintThread printRunnable2 = new TestPrintThread(reentrantLock, printBean, condition2, condition3);
TestPrintThread printRunnable3 = new TestPrintThread(reentrantLock, printBean, condition3, condition1);
printRunnable1.start();
printRunnable2.start();
printRunnable3.start();
}
用newCondition新建三個Condition,每個執行緒持有一個當前執行緒的Condition和下一個執行緒的Condition.當前執行緒列印後,先傳送下一個執行緒的喚醒訊號,在呼叫await進入等待。
Thread[Thread-3,5,main] 0
Thread[Thread-4,5,main] 1
Thread[Thread-5,5,main] 2
Thread[Thread-3,5,main] 3
Thread[Thread-4,5,main] 4
Thread[Thread-5,5,main] 5
...
Thread[Thread-4,5,main] 97
Thread[Thread-5,5,main] 98
Thread[Thread-3,5,main] 99
Thread[Thread-4,5,main] end2
Thread[Thread-5,5,main] end2
Thread[Thread-3,5,main] end2
三個執行緒迴圈列印0,1,2,3,…99.
相關文章
- Java中可重入鎖ReentrantLock原理剖析JavaReentrantLock
- 可重入鎖與不可重入鎖理解
- Java 重入鎖 ReentrantLock 原理分析JavaReentrantLock
- ReentrantLock可重入鎖——原始碼詳解ReentrantLock原始碼
- Java併發程式設計之鎖機制之(ReentrantLock)重入鎖Java程式設計ReentrantLock
- 【java併發程式設計】ReentrantLock 可重入讀寫鎖Java程式設計ReentrantLock
- ReentrantLock可重入鎖、公平鎖非公平鎖區別與實現原理ReentrantLock
- Java多執行緒/併發10、不可重入鎖/自旋鎖、可重入鎖Java執行緒
- Java併發包原始碼學習系列:ReentrantLock可重入獨佔鎖詳解Java原始碼ReentrantLock
- Java synchronized 可重入鎖 基本概念Javasynchronized
- 重入鎖的理解
- java高併發系列 - 第12天JUC:ReentrantLock重入鎖JavaReentrantLock
- Java鎖之ReentrantLock(一)JavaReentrantLock
- Java鎖之ReentrantLock(二)JavaReentrantLock
- ReentrantLock可重入、可打斷、Condition原理剖析ReentrantLock
- Java併發-顯式鎖篇【可重入鎖+讀寫鎖】Java
- redis分散式鎖-可重入鎖Redis分散式
- Lock介面、重入鎖ReentrantLock、讀寫鎖ReentrantReadWriteLockReentrantLock
- java中如何實現可重入的自旋鎖Java
- ReentrantLock(重入鎖)功能詳解和應用演示ReentrantLock
- 可重入鎖原始碼分析原始碼
- 深入理解 ReentrantLockReentrantLock
- Golang可重入鎖的實現Golang
- 從原始碼入手詳解ReentrantLock,一個比synchronized更強大的可重入鎖原始碼ReentrantLocksynchronized
- 【Interview】深入理解ReentrantLockViewReentrantLock
- Java 種15種鎖的介紹:公平鎖,可重入鎖,獨享鎖,互斥鎖等等Java
- Java 種15種鎖的介紹:公平鎖,可重入鎖,獨享鎖,互斥鎖等等...Java
- 【Java面試】什麼是可重入,什麼是可重入鎖? 它用來解決什麼問題?Java面試
- java重入鎖、公平鎖和非公平鎖Java
- Java併發基礎-鎖的使用及原理(可重入鎖、讀寫鎖、內建鎖、訊號量等)Java
- Java併發程式設計,深入理解ReentrantLockJava程式設計ReentrantLock
- 簡單聊聊Java中的ReentrantLockJavaReentrantLock
- Redisson 分散式鎖原始碼 01:可重入鎖加鎖Redis分散式原始碼
- 深入理解Java中的鎖Java
- 深入理解Redis之簡單動態字串Redis字串
- AQS學習(二) AQS互斥模式與ReenterLock可重入鎖原理解析AQS模式
- 深入理解MySQL系列之鎖MySql
- ZooKeeper 分散式鎖 Curator 原始碼 01:可重入鎖分散式原始碼