CountDownLatch(倒数计时器)
CountDownLatch类可以设置一个计数器,然后通过countDown方法来进行减1的操作,使用await方法等待计数器不大于0,然后继续执行await方法之后的语句。
- CountDownLatch 主要有两个方法,当一个或多个线程调用await方法时,这些线程会阻塞
- 其它线程调用countDown方法会将计数器减1(调用countDown方法的线程不会阻塞)
- 当计数器的值变为0时,因await方法阻塞的线程会被唤醒,继续执行
举个栗子:
班长需要等所有同学走后自己才能走, 并且关上教室门
latch的计数最开始是其他同学的数量, 每走一个同学countDown()一次, 班长走的操作在await()后面
public class CountDownLatchDemo{
private static final CountDownLatch latch = new CountDownLatch(5);
public static void main(String[] args) throws InterruptedException{
new Thread(() -> {
while(latch.getCount() > 0){
System.out.println("第" + latch.getCount() + "个同学离开了教室");
latch.countDown(); // 计数减一
}
}, "AAA").start();
latch.await();
// 计数完成之前, await后面的代码将不能执行
new Thread(() -> System.out.println("班长离开教室并锁了门"), "BBB").start();
}
}
CyclicBarrier(循环屏障)
CyclicBarrier允许一组线程全部等待彼此达到共同屏障点的同步辅助。 循环阻塞在涉及固定大小的线程方的程序中很有用,这些线程必须偶尔等待彼此。 屏障被称为循环 ,因为它可以在等待的线程被释放之后重新使用。
CyclicBarrier的构造方法第一个参数是目标障碍数, 每次执行—次await()方法障碍数会减一,如果目标障碍数被清零了,才会执行cyclicBarrier.await() 之后的语句。
一个栗子:
只有集齐七颗龙珠才能召唤神龙
每收集一颗龙珠都会 await(), 直到使用await()使用了七次
public class CyclicBarrierDemo{
private static final CyclicBarrier barrier = new CyclicBarrier(7, ()-> System.out.println("集齐七颗龙珠,可以召唤神龙了"));
public static void main(String[] args){
for(int i = 0; i < 7; i ++){
// 每次循环是一个线程用来收集龙珠
new Thread(() -> {
System.out.println("收集到" + Thread.currentThread().getName() + "颗龙珠");
try{
barrier.await();
} catch(InterruptedException | BrokenBarrierException e){
e.printStackTrace();
}
}, String.valueOf(i + 1)).start();
}
}
}
Semaphore(信号量)
前面使用了 synchronized 关键字和 Lock 锁,在同一时间只允许唯一的线程进入临界区访问资源 (读锁除外),这样子控制的主要目的是为了解决多个线程并发同一资源造成的数据不一致的问题。在另外一种场景下,一个资源有多个副本可供同时使用,比如打印机房有多个打印机、厕所有多个坑可供同时使用,这种情况下,Java 提供了另外的并发访问控制——资源的多副本的并发访问控制,信号量 Semaphore 即是其中的一种。
Semaphore对象在构造时设置可使用的许可证数量 (可以认为这一个对象有多个锁) 用require()方法并且许可证还没被抢完的情况下才能得到许可证, 没有许可证的线程会被阻塞, 使用完后必须使用**release()**释放许可证, 以便别的线程获取许可证
一个栗子:
停车场只有三个停车位, 六辆汽车要停车, 只有没有空位时只能等里面的车回来了才能进去停车
public class SemaphoreDemo{
private static final Semaphore semaphore = new Semaphore(3);
public static void main(String[] args){
for(int i = 0; i < 6; i ++){
new Thread(() -> {
try{
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "抢到了停车位");
int second = new Random().nextInt(3);
Thread.sleep(second * 1000);
System.out.println(Thread.currentThread().getName() + "停了" + second + "小时之后离开了");
} catch(InterruptedException e){
e.printStackTrace();
} finally{
semaphore.release();
}
}, "汽车" + (i + 1)).start();
}
}
}