package com.example.demo.testLock;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class TestReentrantLock {
public static void main(String[] args) {
String[] number = {"1", "2", "3", "4", "5"};
String[] str = {"A", "B", "C", "D", "E"};
ReentrantLock reentrantLock = new ReentrantLock();
Condition condition1 = reentrantLock.newCondition();
Condition condition2 = reentrantLock.newCondition();
new Thread(()->{
reentrantLock.lock();
for (String num : number){
try {
System.out.print(num);
condition2.signal();
condition1.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
condition2.signal();
reentrantLock.unlock();
}).start();
new Thread(()->{
reentrantLock.lock();
for (String s : str){
try {
System.out.print(s);
condition1.signal();
condition2.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
condition1.signal();
reentrantLock.unlock();
}).start();
}
}
上面的代码,网上大部分都能找到,先不关注源码如何实现的(我看不懂),想一个问题,我如果在两个线程外面,包一个for循环,循环10次,是什么打印结果呢? 另外我如何保证两个线程的顺序执行呢?现在只是保证了交替执行,怎么保证先输出A再输出1呢?
第一个问题,开了for循环,假如我们for 10次,会根据线程执行的顺序,交替打印完1A,1A,1A,1A 10次,然后打印2B,2B,2B 10次,当然中间很可能答应出了B2,B2这种顺序
第二个问题,如何保证顺序执行呢?例如我现在一定要保证输出A1,B2,C3,D4,E5这种顺序
答案:加个门栓,CountDownLatch
CountDownLatch countDownLatch = new CountDownLatch(1);
new Thread(()->{
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
reentrantLock.lock();
for (String num : number){
try {
log.info(num);
condition2.signal();
condition1.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
condition2.signal();
reentrantLock.unlock();
}).start();
new Thread(()->{
reentrantLock.lock();
for (String s : str){
try {
log.info(s);
countDownLatch.countDown();
condition1.signal();
condition2.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
condition1.signal();
reentrantLock.unlock();
}).start();
我们现在加了门栓保证了顺序执行,那么外面再加个for循环,会发生什么呢?发现乱序了,没有一个一个顺序输出了,代码如下
@Slf4j
public class TestReentrantLock {
public static void main(String[] args) {
String[] number = {"1", "2", "3", "4", "5"};
String[] str = {"A", "B", "C", "D", "E"};
ReentrantLock reentrantLock = new ReentrantLock();
Condition condition1 = reentrantLock.newCondition();
Condition condition2 = reentrantLock.newCondition();
CountDownLatch countDownLatch = new CountDownLatch(1);
for(int i = 0;i<5;i++){
new Thread(()->{
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
reentrantLock.lock();
for (String num : number){
try {
log.info("countDownLatch:{},num:{}", countDownLatch.getCount(), num);
condition2.signal();
condition1.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
condition2.signal();
reentrantLock.unlock();
}).start();
new Thread(()->{
reentrantLock.lock();
for (String s : str){
try {
countDownLatch.countDown();
log.info("countDownLatch:{},string:{}", countDownLatch.getCount(), s);
condition1.signal();
condition2.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
condition1.signal();
reentrantLock.unlock();
}).start();
}
}
}
16:04:47.264 [Thread-1] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:A
16:04:47.275 [Thread-0] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:1
16:04:47.275 [Thread-2] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:1
16:04:47.275 [Thread-3] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:A
16:04:47.275 [Thread-4] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:1
16:04:47.275 [Thread-5] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:A
16:04:47.275 [Thread-6] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:1
16:04:47.276 [Thread-7] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:A
16:04:47.276 [Thread-8] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:1
16:04:47.276 [Thread-9] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:A
16:04:47.276 [Thread-1] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:B
16:04:47.276 [Thread-0] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:2
16:04:47.276 [Thread-3] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:B
16:04:47.276 [Thread-2] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:2
16:04:47.276 [Thread-5] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:B
16:04:47.276 [Thread-4] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:2
16:04:47.276 [Thread-7] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:B
16:04:47.276 [Thread-6] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:2
16:04:47.277 [Thread-8] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:2
16:04:47.277 [Thread-9] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:B
16:04:47.277 [Thread-0] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:3
16:04:47.277 [Thread-1] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:C
16:04:47.277 [Thread-2] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:3
16:04:47.278 [Thread-3] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:C
16:04:47.278 [Thread-4] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:3
16:04:47.278 [Thread-5] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:C
16:04:47.278 [Thread-7] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:C
16:04:47.278 [Thread-6] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:3
16:04:47.278 [Thread-9] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:C
16:04:47.278 [Thread-8] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:3
16:04:47.278 [Thread-1] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:D
16:04:47.278 [Thread-0] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:4
16:04:47.278 [Thread-3] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:D
16:04:47.278 [Thread-2] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:4
16:04:47.279 [Thread-4] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:4
16:04:47.279 [Thread-5] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:D
16:04:47.279 [Thread-6] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:4
16:04:47.279 [Thread-7] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:D
16:04:47.279 [Thread-8] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:4
16:04:47.279 [Thread-9] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:D
16:04:47.279 [Thread-0] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:5
16:04:47.279 [Thread-1] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:E
16:04:47.279 [Thread-3] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:E
16:04:47.279 [Thread-2] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:5
16:04:47.279 [Thread-5] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:E
16:04:47.279 [Thread-4] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:5
16:04:47.279 [Thread-7] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:E
16:04:47.280 [Thread-6] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:5
16:04:47.280 [Thread-9] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:E
16:04:47.280 [Thread-8] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:5
为什么会出现上面的这种情况,因为你门栓,只是栓了一个线程,当第一个线程执行的时候,门栓变为了0,失去了作用,后面的线程肯定会乱序执行,不会按照你指定的顺序执行了,那么门栓我们调大点,跟循环的次数一致可以么?
例如:CountDownLatch countDownLatch = new CountDownLatch(1);
改为:CountDownLatch countDownLatch = new CountDownLatch(5);
答案是不可以的,因为你放大点,那还是拴住打印字符串的线程,比如我设置5,那底下的线程会打印5次A,然后才执行上面的线程,打印1
我们想要的结果是什么,是这5次循环里面,每次都先打印字符串,然后再打印数字,那么是不是这5次循环,每次的门栓都得是一把新的门栓,然后栓你想要的线程,我们把CountDownLatch countDownLatch = new CountDownLatch(1);放到for循环里面是不是就可以了呢?
答案:依旧是不可以的
两个线程为一组执行的时候,使用的应该是同一个CountDownLatch,对其他线程是不可见的,for循环里,每次实例化一个CountDownLatch,但是最终执行的时候会发现,始终使用的是最后一个CountDownLatch的引用,这是一个问题,为什么?解决了这个问题,就可以保障for循环里面指定输出规则,按照顺序输出了
该如何实现呢?
等这两个线程执行完,然后再次开启下次循环,这样可以,但是效率无法保证了,如何保证一个循环中的两个线程,使用的CountDownLatch是唯一的,每次新建的呢?
使用AtomicReference
AtomicReference<CountDownLatch> atomicReference = new AtomicReference(new CountDownLatch(1));
然后底下用 atomicReference.get().await();和 atomicReference.get().countDown();