JAVA并发包中有三个类用于同步一批线程的行为,分别是闭锁(Latch),信号灯(Semaphore)和栅栏(CyclicBarrier)。本贴主要说明闭锁(Latch)和栅栏(CyclicBarrier)。
1. 闭锁(Latch)
闭锁(Latch) —— 确保多个线程在完成各自事务后,才会打开继续执行后面的内容,否则一直等待。
计数器闭锁(CountDownLatch) —— 是JDK5+ 里面闭锁的一个实现,允许一个或多个线程等待某个事件的发生。CountDownLatch 有个正数的计数器,countDown(); 对计数器做减法操作,await(); 等待计数器 = 0。所有await的线程都会阻塞,直到计数器为0或者等待线程中断或者超时。
1 public static void main(String[] args) throwsInterruptedException {2 //申明,等待事件数量 5次
3 CountDownLatch await = new CountDownLatch(5);4
5 //依次创建并启动处于等待状态的5个MyRunnable线程
6 for (int i = 1; i < 6; ++i) {7 new Thread(newMyRunnable(await, i)).start();8 }9
10 System.out.println("等待线程开始工作......");11 await.await();12 System.out.println("结束!");13 }
1 public static class MyRunnable implementsRunnable {2
3 private finalCountDownLatch await;4 private final intnum;5
6 public MyRunnable(CountDownLatch await, intnum) {7 this.await =await;8 this.num =num;9 }10
11 public voidrun() {12 try{13 System.out.println("线程"+num+"执行完毕。");14 await.countDown(); //当前事件执行完毕,计数 -1
15 } catch(InterruptedException e) {16 e.printStackTrace();17 }18 }19 }
运行结果:
等待线程开始工作......
线程1执行完毕。
线程2执行完毕。
线程3执行完毕。
线程4执行完毕。
线程5执行完毕。
结束!
流程如图所示:
图1 - CountDownLatch 处理流程
2. 栅栏(CyclicBarrier)
栅栏类似于闭锁,它能阻塞一组线程直到某个事件发生。 栅栏与闭锁的关键区别在于,所有的线程必须同时到达栅栏位置,才能继续执行。闭锁用于等待事件,而栅栏用于等待其他线程。
场景: 比如甲乙丙三人一把椅子,甲做椅子腿,乙做椅子面,丙做椅子靠背。等3人都做成后,就可以组装成椅子了。这是一种并行迭代,将一个问题分成很多子问题,当一系列的子问题都解决之后(所有子问题线程都已经await(); ),此时将栅栏打开,所有子问题线程被释放,而栅栏位置可以留着下次使用。
示例如下:
1 public static void main(String[] args) throwsInterruptedException {2 //申明,等待线程数量 3次
3 CyclicBarrier cyclicBarrier = new CyclicBarrier(3);4
5 //依次创建并启动处于等待状态的3个MyRunnable2线程
6 new Thread(new ChairRunnable(cyclicBarrier, "椅子腿")).start();7 new Thread(new ChairRunnable(cyclicBarrier, "椅子面")).start();8 new Thread(new ChairRunnable(cyclicBarrier, "椅子背")).start();9 }
1 public static class ChairRunnable implementsRunnable {2 private finalCyclicBarrier cyclicBarrier;3 private finalString event;4
5 publicChairRunnable(CyclicBarrier cyclicBarrier, String event) {6 this.cyclicBarrier =cyclicBarrier;7 this.event =event;8 }9
10 public voidrun() {11 try{12 System.out.println("开始做【" + event + "】。");13 Thread.sleep(new Random().nextInt(10000));14 cyclicBarrier.await(); //等待其他线程完成
15 } catch(InterruptedException e) {16 e.printStackTrace();17 } catch(BrokenBarrierException e) {18 e.printStackTrace();19 }20 System.out.println("【" + event + "】做好了, 我们来一起组装吧!");21 }22 }
运行结果:
开始做【椅子腿】。
开始做【椅子背】。
开始做【椅子面】。
【椅子面】做好了, 我们来一起组装吧!
【椅子腿】做好了, 我们来一起组装吧!
【椅子背】做好了, 我们来一起组装吧!
流程如下图所示:
图2 - CyclicBarrier 处理流程