本文主要讨论在高并发编程中两非常实用工具CyclicBarrier(同步屏障)和CountDownLatch(倒计时锁),两者都是java.util.concurrent并发包内非常有用的并发工具类,为了帮助理解会结合一些有趣的比喻,下面将对两者进行讨论。
[[276796]]
一、CountDownLatch倒计时锁(一个线程等待另外N个线程完成某个事情之后才能执行)
- //创建一个倒计时锁,设置值为5
- final CountDownLatch latch = new CountDownLatch(5);
- try {
- //启用5个线程
- for (int i = 1; i <= 5; i++) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- try {
- Thread.sleep(1000);
- } catch (Exception e) {
- e.printStackTrace();
- }
- System.out.println("子线程执行!");
- //让latch锁中的数值减1
- latch.countDown();
- }
- }).start();
- }
- //处于阻塞状态直到latch中数值为零才执行后续操作
- latch.await();
- System.out.println("主线程执行");
- } catch (Exception e) {
- System.out.println("捕获异常");
- }
- }
运行结果:
解析:倒计时锁理解起来比较容易,这里通过结合实际场景帮助理解。场景:一张数据表中存放大量的数据,现要读取表里的所有信息。为了提高读取效率便通过在主线程中开启多个子线程分工合作对数据表进行读取。接下来要等待全部子线程读取完毕之后,将读取到的内容进行汇总并在主线程中进行处理。
[[276797]]
二、可循环使用的屏障CyclicBarrier(N个线程相互等待,任何一个线程完成之前,所有的线程都必须等待)
- //建立一个屏障并设定一个值,当有足够的线程达到屏障时再一起释放
- CyclicBarrier barrier = new CyclicBarrier(5, () -> {
- System.out.println("开始游戏");
- });
- ExecutorService executorPool = Executors.newCachedThreadPool();
- for (int i = 1; i <= 5; i++) {
- int num = i;
- Thread.sleep(1000);
- executorPool.execute(() -> {
- try {
- System.out.println(num + "号玩家,已准备好,等待进入游戏");
- barrier.await();
- System.out.println(num + "号玩家,已经进入游戏");
- } catch (InterruptedException e) {
- e.printStackTrace();
- } catch (BrokenBarrierException e) {
- e.printStackTrace();
- }
- });
- }
- executorPool.shutdown();
运行结果:
解析:这里通过一个形象的例子来帮助理解CyclicBarrier。
三、两者对比
最后
CyclicBarrier(同步屏障)和CountDownLatch(倒计时锁)都是不错的高并发编程工具类,两者很相似容易造成混淆,通过理解两者各自工作方式和特点并结合业务需求合理地应用他们会有不错的效益。