学而不思则罔,思而不学则殆
【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.