CountDownLatch、CyclicBarrier和Semaphore
1、CountDownLatch用法
类似于计数器,比如某个任务需要等待另外N个任务执行完后再继续执行,就可以用CountDownLatch实现。
构造方法:
//count为计数器值
public CountDownLatch(int count) { if (count < 0) throw new IllegalArgumentException("count < 0"); this.sync = new Sync(count); }
另外还有三个主要方法:
//调用该方法的线程会进入阻塞状态,等到count值为0时才会继续执行 public void await() throws InterruptedException { sync.acquireSharedInterruptibly(1); } //作用同上一个方法,只不过加了超时时间,即使count不为0但超过timeout后也会继续执行 public boolean await(long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout)); } //将计数器count值减一 public void countDown() { sync.releaseShared(1); }
Demo:比如五个工人干活,工头要等他们一起全部干完活才结算工资
public class Demo { public static void main(String[] args) { CountDownLatch countDownLatch = new CountDownLatch(5); for (int i = 0; i < 5; i ++) { final int worker = i; new Thread(() -> { System.out.println("工人" + worker + "开始工作"); try{ //睡眠模拟干活 Thread.sleep(worker * 1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("工人" + worker + "完成工作"); countDownLatch.countDown(); }).start(); } try { countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("所有工作已完成,工头结算工资"); } }
打印结果:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
工人0开始工作
工人1开始工作
工人0完成工作
工人4开始工作
工人2开始工作
工人3开始工作
工人1完成工作
工人2完成工作
工人3完成工作
工人4完成工作
所有工作已完成,工头结算工资
2、CyclicBarrier
它是针对在某一组任务内相互等待,直到这一组任务都执行到某一个设定的点(调用await方法)之后再继续各自执行后续操作。CyclicBarrier是可重用的,CountDownLatch则不行。
构造方法:
//构造参数为每组任务的任务数 public CyclicBarrier(int parties) { this(parties, null); } //barrierAction:可以在该组任务到达指定点后由最晚到达的线程执行额外的操作 public CyclicBarrier(int parties, Runnable barrierAction) { if (parties <= 0) throw new IllegalArgumentException(); this.parties = parties; this.count = parties; this.barrierCommand = barrierAction; }
关键方法:
//调用该方法后线程进入等待,直到该组所有线程都执行到这一句再开始后续操作 public int await() throws InterruptedException, BrokenBarrierException { try { return dowait(false, 0L); } catch (TimeoutException toe) { throw new Error(toe); // cannot happen } } //同上个方法,多加了一个超时时间,超过设定时间直接执行后续操作 public int await(long timeout, TimeUnit unit) throws InterruptedException, BrokenBarrierException, TimeoutException { return dowait(true, unit.toNanos(timeout)); }
Demo:比如体育课老师让学生跑步,全部一起跑完才可以各自自由活动
public class Demo { public static void main(String[] args) { CyclicBarrier cyclicBarrier = new CyclicBarrier(3, () -> { System.out.println(Thread.currentThread().getName() + "告诉老师,所有人已完成跑步"); }); for (int i = 0; i < 3; i ++) { final int stu = i; new Thread(() -> { System.out.println("学生" + stu + "开始跑步"); try{ //睡眠模拟跑步 Thread.sleep(2000); System.out.println("学生" + stu + "已到达终点"); cyclicBarrier.await(); } catch (Exception e) { e.printStackTrace(); } System.out.println("所有人到达终点,学生" + stu + "自由活动"); }, "学生" + stu).start(); } } }
打印结果:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
学生0开始跑步
学生1开始跑步
学生2开始跑步
学生2已到达终点
学生1已到达终点
学生0已到达终点
学生0告诉老师,所有人已完成跑步
所有人到达终点,学生0自由活动
所有人到达终点,学生1自由活动
所有人到达终点,学生2自由活动
3、Semaphore
信号量,可以控制同时执行的线程数量,类似于锁。
构造方法:
//permits为许可个数,默认为非公平的 public Semaphore(int permits) { sync = new NonfairSync(permits); } //fair表示是否公平的,公平的就是FIFO public Semaphore(int permits, boolean fair) { sync = fair ? new FairSync(permits) : new NonfairSync(permits); }
几个关键方法:
//获取一个许可,获取不到时会进入阻塞等待 public void acquire() throws InterruptedException { sync.acquireSharedInterruptibly(1); } //获取permits个许可 public void acquire(int permits) throws InterruptedException { if (permits < 0) throw new IllegalArgumentException(); sync.acquireSharedInterruptibly(permits); } //释放一个许可 public void release() { sync.releaseShared(1); } //释放permits个许可 public void release(int permits) { if (permits < 0) throw new IllegalArgumentException(); sync.releaseShared(permits); }
Demo:银行柜台每个服务窗口同一时间只能服务一个顾客,其他顾客只能等待
public class Demo { public static void main(String[] args) throws Exception { //三个服务窗口 Semaphore semaphore = new Semaphore(3); //9个顾客办理业务 for (int i = 0; i < 9; i ++) { final int customer = i; new Thread(() -> { try{ semaphore.acquire(); System.out.println("顾客" + customer + "获得许可,柜台开始服务"); //睡眠模拟柜台服务 Thread.sleep(2000); semaphore.release(); System.out.println("顾客" + customer + "业务已办理完成,释放许可"); } catch (Exception e) { e.printStackTrace(); } }).start(); } } }
打印结果:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
顾客0获得许可,柜台开始服务
顾客4获得许可,柜台开始服务
顾客2获得许可,柜台开始服务
顾客2业务已办理完成,释放许可
顾客0业务已办理完成,释放许可
顾客3获得许可,柜台开始服务
顾客4业务已办理完成,释放许可
顾客1获得许可,柜台开始服务
顾客5获得许可,柜台开始服务
顾客1业务已办理完成,释放许可
顾客5业务已办理完成,释放许可
顾客6获得许可,柜台开始服务
顾客7获得许可,柜台开始服务
顾客3业务已办理完成,释放许可
顾客8获得许可,柜台开始服务
顾客8业务已办理完成,释放许可
顾客6业务已办理完成,释放许可
顾客7业务已办理完成,释放许可