前言
CountDownLatch 和 CyclicBarrier 这两个东西,面试必问,倒也不难,反正大家都是背的,面试官也知道你是背的,你也知道面试官知道你是背的,面试官也知道你知道面试官知道你是背的.......
无限套娃
CountDownLatch
CountDownLatch,闭锁,是一个同步工具类,用来协调多个线程之间的同步。
主要有两个方法,当一个或者多个线程调用 await 方法时,调用线程会被阻塞,其他线程调用 countDown 方法计数器 - 1,当计数器的值变为0时,调用 await 方法被阻塞的线程会被唤醒,继续执行
在完成一组计算时,允许一个线程或者多个一直等待,直到其他线程全部完成。
例子:将一个六人的队伍集合带回,必须人齐了才能带回
如果不使用闭锁的话:
csharp
public class CountDownLatchTest {
public static void main(String[] args) {
for (int i = 0; i < 6; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "到了");
}).start();
}
System.out.println(Thread.currentThread().getName() + "六人队伍齐了");
}
}
输出结果:
css
Thread-0到了
Thread-4到了
Thread-3到了
Thread-2到了
Thread-1到了
main六人队伍齐了
Thread-5到了
很显然,线程是竞争执行的,不一定哪个块,无法保证主线程最后执行。
我们加上 CountDownLatch
scss
public static void main(String[] args) {
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 0; i < 6; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "到了");
countDownLatch.countDown();
}).start();
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + "六人队伍齐了");
}
输出结果:
css
Thread-0到了
Thread-2到了
Thread-3到了
Thread-5到了
Thread-1到了
Thread-4到了
main六人队伍齐了
CyclicBarrier
CyclicBarrier,循环屏障,它允许一组线程互相等待,直到到达某个公共屏障点。好比到了某个条件之后,所有线程一起执行
例子:还是6个人去干饭,人齐了去干饭,人齐之后下达命令,然后大家干饭
csharp
public class CyclicBarrierTest {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(6, () -> {
System.out.println("人齐了,下达干饭命令,出发干饭!");
});
for (int i = 0; i < 6; i++) {
int finalI = i;
new Thread(() -> {
System.out.println("老"+ finalI +"来到");
try {
cyclicBarrier.await();
System.out.println("老"+ finalI +"干饭");
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (BrokenBarrierException e) {
throw new RuntimeException(e);
}
}).start();
}
}
}
输出结果:
老0来到
老3来到
老2来到
老1来到
老4来到
老5来到
人齐了,下达干饭命令,出发干饭!
老5干饭
老0干饭
老3干饭
老2干饭
老4干饭
老1干饭
可以看到等人齐了之后,输出干饭命令,然后各个线程再去执行剩下的代码
区别和联系
可以看一下 CountDownLatch 的代码
csharp
public class CountDownLatchTest {
public static void main(String[] args) {
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 0; i < 6; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "到了");
countDownLatch.countDown();
System.out.println(Thread.currentThread().getName() + "到了之后出发");
}).start();
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + "六人队伍齐了");
}
}
输出结果:
css
Thread-3到了
Thread-0到了
Thread-0到了之后出发
Thread-5到了
Thread-5到了之后出发
Thread-2到了
Thread-2到了之后出发
Thread-4到了
Thread-4到了之后出发
Thread-3到了之后出发
Thread-1到了
Thread-1到了之后出发
main六人队伍齐了
可以看到使用 CountDownLatch ,调用 countDown()方法之后,之后的代码仍会继续执行,CountDownLatch 只能保证一个线程或几个线程最后执行,其他调用countDown()方法的线程早就执行结束了。
而 CyclicBarrier 的 await() 方法会阻塞,等所有线程执行结束后,所有调用 await() 线程会一起执行。
相同点:
CountDownLatch 和 CyclicBarrier 都具有await()方法,执行此方法都会阻塞当前线程,当达到某种条件才能继续执行
不同点:
- CountDownLatch 是一次性的,CyclicBarrier 是可循环利用的
- CountDownLatch 的计数器只能使用一次,而CyclicBarrier 的计数器可以使用 reset方法重置,能够处理更为复杂的场景
- CountDownLatch一般用于某个线程A等待若干个其他线程执行完任务之后,它才执行;而CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行;
- CountDownLatch强调一个线程等多个线程完成某件事情。CyclicBarrier是多个线程互等,等大家都完成,再携手共进
总结和提高
CountDownLatch与CyclicBarrier都是用于控制并发的工具类,都可以理解成维护的就是一个计数器,可以用来控制线程的执行。