CountDownLatch, CyclicBarrier, Semaphore是什么
控制线程之间通信的工具类,在juc包下
CountDownLatch
作用:用来控制线程顺序 ( 一堆线程运行,当其他所有线程运行完成后,在执行指定的线程 )
一句话总结:班长最后离开教室
案例:现在有6个人和班长在教室里上自习,要求 班长等所有学生离开教室后,才能锁门走人
//问题代码:
public static void main(String[] args) {
for (int i = 1; i <= 6; i++) {
int num = i;
new Thread(() -> {
System.out.println("第" + num +"个人离开教室");
}, String.valueOf(i)).start();
}
System.out.println("班长离开教室");
}
//出现结果:
//班长最后一个走只是几率事件,有可能出现的情况是:班长没有等到最后一个人离开自己就已经把教室的门锁上了
解决方案:使用CountDownLatch
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 1; i <= 6; i++) {
int num = i;
new Thread(() -> {
System.out.println("第" + num + "个人离开教室");
//表示开始计数,每执行一次,就计数一次
countDownLatch.countDown();
}, String.valueOf(i)).start();
}
//表示次线程进入阻塞状态,只有当其他线程全部都走完了之后,才能执行此线程
countDownLatch.await();
System.out.println("班长离开教室");
}
//countDownLatch.countDown() 计数了6次后, 被await的线程才会继续执行
使用 await ()方法执行结果如图所示
使用 await(long timeout, TimeUnit unit) 方法执行结果如图所示
主要方法:
新建一个计数器 new CountDownLatch ( int I ) ;
表示创建一个CountDownLatch执行器 ( ) 中的参数表示等待的线程数
执行计数方法 public void countDown() :
表示程序开始计数,每执行完成一次线程,都需要调用一次这个方法,构造方法中调用的参数是几,就需要调用这个方法几次
线程进入阻塞状态 public void await()
表示让其他线程进入阻塞状态
进入阻塞状态扩展方法:public boolean await(long timeout, TimeUnit unit)
表示让其他线程进入阻塞状态,但是 如果阻塞的时间达到了设定的时间,不管计数是否完成,都会执行被阻塞的线程
注意点
- CountDownLatch 的构造方法中() 中的int类型 需要和等待的子线程数一致
- 如果不一致的话 x > 等待线程数 等待线程会处于阻塞状态,不能继续执行下去
- 如果 x < 等待线程数,等待线程会和其他线程提前开始抢占CPU资源
CountDownLatch 执行原理
CountDownLatch主要有2个方法 ( countDown方法和await方法 ) ,当一个或多个线程调用 await 方法时,这些线程会阻塞。
其他线程调用countDown方法会将计数器减1 ( 调用countDown方法的线程不会阻塞 )
当计数器的值变为0时,因await方法阻塞的线程会被唤醒,继续执行
CyclicBarrier
一句话总结:集齐七颗龙珠才能召唤神龙
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(6, () -> {
System.out.println("神龙啊");
});
for (int i = 1; i <= 6; i++) {
int num = i;
new Thread(() -> {
System.out.println("集齐第" + num + "颗龙珠");
try {
cyclicBarrier.await();
} catch (Exception e) {
e.printStackTrace();
}
}, String.valueOf(i)).start();
}
}
执行结果如下
主要作用方法:
new CyclicBarrier(int parties, Runnable barrierAction)
表示创建一个循环栅栏,计数完成后 执行Runnable中的内容
public int await()
表示 让其他线程进入阻塞状态
CountDownLatch 和 CyclicBarrier 和 semaphore 的区别是什么
- CountDownLatch 计数器依次
递减
,最终执行其他线程中的操作 - CyclicBarrier 循环栅栏依次
递增
,最终执行 Runnable 线程中的内容 - Semaphore 有加有减的情况
semaphore
一句话总结:抢车位
举个例子:现在有6部车,但是只有3个停车位,要求前3辆车先停在车位上 停2秒中后,另外3辆车继续抢占这3个车位,直到每个车都停了3秒中后,执行完成
public static void main(String[] args) {
//模拟有3个空车位
Semaphore semaphore = new Semaphore(3);
//6个线程来抢3个资源
for (int i = 1; i <= 6; i++) {
int num = i;
new Thread(() -> {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "抢到了第" + num + "个车位");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + "离开了");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
semaphore.release();
}
}, String.valueOf(i)).start();
}
}
执行结果如下
主要方法:
public Semaphore(int permits)
创建一个信号量, permits 表示的是共享资源的数量
public void acquire()
当一个线程调用 acquire操作时,他要么成功获取信号量 ( 信号量 减1 ),要么一直等下去,直到有线程释放信号量 或超时
public void release()
release ( 释放 ) 实际上会将信号量的值 加1,然后唤醒等待的线程
Semaphore的作用
- 用于多个共享资源的互斥使用,
- 用于并发线程数的控制(如果不控制线程数的话,可能会导致线程吧服务器冲垮了)