1. ContDownLatch(倒计时器)
ContDownLatch是一个同步辅助类,在完成某些运算时,只有其他所有线程的运算全部完成,当前运算才继续执行,这就叫闭锁。
● CountDownLatch(int count):实例化一个倒计数器,count指定计数个数
● countDown():计数减一
● await():等待,当计数减到0时,所有线程并行执行
代码演示:火箭发射
public class ConutDownLatchDemo { public static void main(String[] args) throws InterruptedException { // 倒计时器:计数数量为10 CountDownLatch latch = new CountDownLatch(10); // 倒计时检查任务,使用10个线程来完成任务 CountRunnable runnable = new CountRunnable(latch); for (int i = 0; i < 10; i++) { new Thread(runnable).start(); // 执行任务 } // 等待检查完成 latch.await(); // 发射火箭 System.out.println("Fire!"); } } class CountRunnable implements Runnable { private CountDownLatch countDownLatch; public CountRunnable(CountDownLatch countDownLatch) { this.countDownLatch = countDownLatch; } @Override public void run() { // 模拟检查任务 try { Thread.sleep(new Random().nextInt(10) * 1000); System.out.println("check complete"); } catch (InterruptedException e) { e.printStackTrace(); } finally { // 计数 -1 // 放在finally避免任务执行过程出现异常,导致countDown()不能被执行 countDownLatch.countDown(); } } }
上述代码中我们先生成了一个CountDownLatch实例。计数数量为10,这表示需要有10个线程来完成任务,等待在CountDownLatch上的线程才能继续执行。latch.countDown();方法作用是通知CountDownLatch有一个线程已经准备完毕,倒计数器可以减一了。latch.await()方法要求主线程等待所有10个检查任务全部准备好才一起并行执行。
2. CyclicBarrier(加法计数器)
一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。
使用场景:
需要所有的子任务都完成时,才执行主任务,这个时候就可以选择使用CyclicBarrier。
代码演示:集龙珠召唤神龙
public class CyclicBarrierDemo { public static void main(String[] args) { // 加法计数器,集齐7颗龙珠召唤神龙 CyclicBarrier cyclicBarrier = new CyclicBarrier(7, new Runnable() { @Override public void run() { System.out.println("集齐龙珠 -> 召唤神龙!嗷!嗷!嗷!!!"); } }); for (int i = 0; i < 7; i++) { new Thread(() -> { System.out.println("获得(" + Thread.currentThread().getName() + ")星龙珠"); try { cyclicBarrier.await(); // 等待其它线程执行完成 } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } }, String.valueOf(i + 1)).start(); } } }
◇ CountDownLatch 和 CyclicBarrier 比较
① CountDownLatch 是线程组之间的等待,即一个(或多个)线程等待N个线程完成某件事情之后再执行。
CyclicBarrier 则是线程组内的等待,即每个线程相互等待,即 N 个线程都被拦截之后,然后依次执行。
② CountDownLatch 是减计数方式。
CyclicBarrier 是加计数方式。
③ CountDownLatch 计数为 0 无法重置。
CyclicBarrier 计数达到初始值,则可以重置。
④ CountDownLatch 不可以复用,
CyclicBarrier 可以复用。
3. Semaphore(信号量)
Semaphore 是一个计数信号量,必须由获取它的线程释放。
作用:多个共享资源互斥的使用!并发限流,限制可以访问某些资源的线程数量。
Semaphore 只有3个操作:① 初始化 ② 增加 ③ 减少
常用方法:
public void acquire();// 获取一个许可 public void acquire(intpermits);// 获取permits个许可 public void release();// 释放一个许可 public void release(intpermits);// 释放permits个许可
- acquire:用来获取一个许可,若无许可能够获得,则会一直等待,直到获得许可。
- release:用来释放许可。注意,在释放许可之前,必须先获获得许可。
这4个方法都会被阻塞,如果想立即得到执行结果,可以使用下面几个方法:
// 尝试获取一个许可,若获取成功,则立即返回true,若获取失败,则立即返回false public boolean tryAcquire(); // 尝试获取一个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false public boolean tryAcquire(longtimeout, TimeUnit unit); // 尝试获取permits个许可,若获取成功,则立即返回true,若获取失败,则立即返回false public boolean tryAcquire(intpermits); // 尝试获取permits个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false public boolean tryAcquire(intpermits, longtimeout, TimeUnit unit);
代码演示:抢车位
现有6辆车,但是只有3个停车位
public class SemaphoreDemo { public static void main(String[] args) { // 信号量,只允许 3 个线程同时访问 Semaphore semaphore = new Semaphore(3); for (int i = 0; i < 6; i++) { new Thread(() -> { try { semaphore.acquire(); // 获取许可,将信号量 +1,如果信号量已经满了,等待被释放为止! System.out.println("Car" + Thread.currentThread().getName() + " >>> 抢到车位"); TimeUnit.SECONDS.sleep(new Random().nextInt(10)); System.out.println("离开车位 >>> Car" + Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); } finally { semaphore.release(); // 释放,将信号量 -1,然后唤醒等待的线程! } }, String.valueOf(i)).start(); } } }
◇ 总结
CountDownLatch 是一个线程等待其他线程, CyclicBarrier 是多个线程互相等待。
CountDownLatch 的计数是减 1 直到 0,CyclicBarrier 是加 1,直到指定值。
CountDownLatch 是一次性的, CyclicBarrier 可以循环利用。
CyclicBarrier 可以在最后一个线程达到屏障之前,选择先执行一个操作。
Semaphore ,需要拿到许可才能执行,并可以选择公平和非公平模式。