CountDownLatch
CountDownLatch的作用就是保证其他的线程都执行完毕,主线程才会执行,只要有一个线程没有执行完毕,主线程都需要等待此线程执行完毕后才能执行。
场景:加入一个班里有5个人,班长必须等到所有的人离开了教室之后才可以锁门。
下面看一下代码模拟这个场景:
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "上完自习,离开教室!");
},String.valueOf(i)).start();
}
System.out.println("班长锁门了!");
}
执行结果:
0上完自习,离开教室!
3上完自习,离开教室!
2上完自习,离开教室!
班长锁门了!
1上完自习,离开教室!
4上完自习,离开教室!
假如这个事情发生在了生活中,估计1号和4号同学会打班长一顿吧。
那么这个问题就需要使用CountDownLatch来解决了。
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(5);
for (int i = 0; i < 5; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "上完自习,离开教室!");
// 一个同学离开教室之后,countDownLatch减少1
countDownLatch.countDown();
},String.valueOf(i)).start();
}
// 保证countDownLatch减为0,否则会一直等待
countDownLatch.await();
System.out.println("班长锁门了!");
}
输出结果:
0上完自习,离开教室!
1上完自习,离开教室!
2上完自习,离开教室!
3上完自习,离开教室!
4上完自习,离开教室!
班长锁门了!
这样孩子们就可以吃上麻麻做的香喷喷的饭了!
总结:让一些线程阻塞知道另一些线程完成一系列操作后才被唤醒。
CountDownLatch主要有两个方法,当一个或多个线程调用await方法时,调用线程会被阻塞。其他线程调用countDown方法会将计数器减1,(调用countDown方法的线程不会被阻塞),当计数器的值变为0时,因调用await方法被阻塞的线程会被唤醒,继续执行。
CyclicBarrier
CyclicBarrier正好和CountDownLatch相反,是做加法,就像是集齐了七颗龙珠才能召唤神龙。
CyclicBarrier的字面意思是可循环使用的屏障,他要做的事情是让一组线程到达 一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程达到屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活,线程进入屏障通过CyclicBarrier的await方法。
public static void main(String[] args) throws BrokenBarrierException {
CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> System.out.println("召唤神龙!"));
for (int i = 1; i <= 7; i++) {
int finalI = i;
new Thread(() -> {
System.out.println("收集到" + finalI + "星球!");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
运行结果:
收集到1星球!
收集到3星球!
收集到2星球!
收集到4星球!
收集到5星球!
收集到6星球!
收集到7星球!
召唤神龙!
Semaphore
Semaphore 是 synchronized 的加强版,作用是控制线程的并发数量。就这一点而言,单纯的synchronized 关键字是实现不了的。你可以把Semaphore当成前台的管理人员,他的作用就是调度客人,假如我们去海底捞吃饭,很不巧,我们去的时候已经满了,需要等位,前台会给我们一个号码等待叫号,有一桌客人吃完管理人员通知下一个等号的人进去吃饭。
场景: 假如一个停车场有三个停车位,现在有6辆车需要停车,那这样的话肯定有3辆车需要等待,假如进入停车场的车每辆停3秒,3秒后离开停车场,这样剩余的3辆车就会有空余的车位进行停车。
public static void main(String[] args) throws BrokenBarrierException {
Semaphore semaphore = new Semaphore(3);
for (int i = 1; i <= 6; i++) {
new Thread(() -> {
try {
// 占车位
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "进场!");
// 停车3秒
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName() + "离开!");
} catch (InterruptedException e) {
e.printStackTrace();
}
finally {
// 释放车位
semaphore.release();
}
},String.valueOf(i)).start();
}
}
运行结果:
1进场!
2进场!
3进场!
2离开!
1离开!
4进场!
3离开!
5进场!
6进场!
4离开!
6离开!
5离开!