CountDownLatch(倒计数器) - 人去楼空:人都走没了,才能锁门
作用:一个线程(或者多个), 等待另外N个线程完成某个事情之后才能执行。
实例:学生放学了,老师等所有学生都离开后,最后离开并锁门(走一个减1,直到走完为0)
代码:
public class CountDownLatchDemo1 {
private static int COUNT = 8; // 该教室有8个学生
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(COUNT);
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < COUNT; i++) {
executorService.submit(new GotoMeat(String.valueOf(i), latch));
}
latch.await();
executorService.shutdown();
System.out.println("学生都走了,老师可以锁门了!");
}
public static class GotoMeat implements Runnable{
private String name;
private CountDownLatch latch;
public GotoMeat(String name, CountDownLatch latch) {
this.name = name;
this.latch = latch;
}
@Override
public void run() {
System.out.println("学生 " + name + " 已经离开... ");
latch.countDown();
}
}
}
Semaphore(信号量,限流) - 食堂打饭:只有3个窗口可以同时打饭
作用:Semaphore用于限制可以访问某些资源(物理或逻辑的)的线程数目,他维护了一个许可证集合,有多少资源需要限制就维护多少许可证集合,假如这里有N个资源,那就对应于N个许可证,同一时刻也只能有N个线程访问。一个线程获取许可证就调用acquire方法,用完了释放资源就调用release方法。
实例:高速路收费站有5个收费口,不管后面有多少辆车,每次只能通行5辆
(图片来自网络)
代码:
public class SemaphoreDemo1 {
private static int COUNT = 5; // 共有5辆车等待通过收费口
private static int LIMIT = 2; // 有2个收费口
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(LIMIT);
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < COUNT; i++) {
executorService.submit(new PassThroughTheToll(String.valueOf(i), semaphore));
}
executorService.shutdown();
}
public static class PassThroughTheToll implements Runnable {
private String name;
private Semaphore semaphore;
public PassThroughTheToll(String name, Semaphore semaphore) {
this.name = name;
this.semaphore = semaphore;
}
@Override
public void run() {
try {
semaphore.acquire();
System.out.println("车辆 " + name + "拿到了通过收费站的许可...");
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("车辆 " + name + "通过收费站,释放了许可~~~");
semaphore.release();
}
}
}
}
CyclicBarrier(屏障) - 运动员互相等待就位:全就位后,裁判发枪
作用:CyclicBarrier允许一组线程在到达某个栅栏点(common barrier point)互相等待,直到最后一个线程到达栅栏点,栅栏才会打开,处于阻塞状态的线程恢复继续执行。
实例:师徒四人,都准备好了(到达公共屏障点common barrier point,相当于一个障碍点),再一起走(在障碍点一个都不能少,到齐后才能一起干大事)
实例2:田径比赛,各位运动员都准备好就位之后,裁判发枪
(图片来源互联网)
代码:
public class CyclicBarrierDemo1 {
private static int COUNT = 4; // 师徒四人
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(COUNT);
String[] names = {"唐老板", "猴哥", "八戒", "沙和尚"};
ExecutorService executorService = Executors.newFixedThreadPool(COUNT);
for (int i = 0; i < COUNT; i++) {
executorService.submit(new Travel(names[i], cyclicBarrier));
}
executorService.shutdown();
}
static class Travel implements Runnable {
private String name;
private CyclicBarrier cyclicBarrier;
public Travel(String name, CyclicBarrier cyclicBarrier) {
this.name = name;
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(1+(new Random().nextInt(3)));
System.out.println(name + "已就位...,等待其他成员到来");
cyclicBarrier.await();
System.out.println(name + " :看到所有成员已到齐,四人一起过流沙河");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
}
CountDownLatch 与 CyclicBarrier 的区别
CountDownLatch | CyclicBarrier |
---|---|
减计数方式 | 加计数方式 |
计数为0时,释放所等有待的线程 | 计数达到指定值时,释放所有等待的线程 |
计数为0时,无法重置 | 计数达到指定值时,计数置为0,重新开始 |
调用countDown()方法减1 调用await()方法只进行阻塞,对计数没任何影响 | await()方法计数加1, 若加1后的值不等于初始指定的值,则线程阻塞 |
不可重复利用 | 可重复利用 |
CountDownLatch是计数器,线程完成一个记录一个,只不过计数不是递增而是递减;
CyclicBarrier更像是一个阀门,需要所有线程都要到达,阀门才能打开,然后继续执行。