JUC包下的线程协作计数CountDownLatch及CyclicBarrier

CountDownLatch

概述

用来进行线程同步协作,等待所有线程完成倒计时。

其中构造参数用来初始化等待计数值,await() 用来等待计数归零,countDown() 用来让计数减一

CountDownLatch不能重用,只能减不能加

public static void main(String[] args) throws InterruptedException {
    CountDownLatch latch = new CountDownLatch(3);
    new Thread(() -> {
        log.debug("begin...");
        sleep(1);
        latch.countDown();
        log.debug("end...{}", latch.getCount());
    }).start();
    new Thread(() -> {
        log.debug("begin...");
        sleep(2);
        latch.countDown();
        log.debug("end...{}", latch.getCount());
    }).start();
    new Thread(() -> {
        log.debug("begin...");
        sleep(1.5);
        latch.countDown();
        log.debug("end...{}", latch.getCount());
    }).start();
    log.debug("waiting...");
    latch.await();
    log.debug("wait end...");
}

输出

18:44:00.778 c.TestCountDownLatch [main] - waiting...
18:44:00.778 c.TestCountDownLatch [Thread-2] - begin...
18:44:00.778 c.TestCountDownLatch [Thread-0] - begin...
18:44:00.778 c.TestCountDownLatch [Thread-1] - begin...
18:44:01.782 c.TestCountDownLatch [Thread-0] - end...2
18:44:02.283 c.TestCountDownLatch [Thread-2] - end...1
18:44:02.782 c.TestCountDownLatch [Thread-1] - end...0
18:44:02.782 c.TestCountDownLatch [main] - wait end... 

可以配合线程池使用,改进如下

public static void main(String[] args) throws InterruptedException {
    CountDownLatch latch = new CountDownLatch(3);
    ExecutorService service = Executors.newFixedThreadPool(4);
    service.submit(() -> {
        log.debug("begin...");
        sleep(1);
        latch.countDown();
        log.debug("end...{}", latch.getCount());
    });
    service.submit(() -> {
        log.debug("begin...");
        sleep(1.5);
        latch.countDown();
        log.debug("end...{}", latch.getCount());
    });
    service.submit(() -> {
        log.debug("begin...");
        sleep(2);
        latch.countDown();
        log.debug("end...{}", latch.getCount());
    });
    service.submit(()->{
        try {
            log.debug("waiting...");
            latch.await();
            log.debug("wait end...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });
}

应用

等待多线程准备完毕

AtomicInteger num = new AtomicInteger(0);
ExecutorService service = Executors.newFixedThreadPool(10, (r) -> {
    return new Thread(r, "t" + num.getAndIncrement());
});
CountDownLatch latch = new CountDownLatch(10);
String[] all = new String[10];
Random r = new Random();
for (int j = 0; j < 10; j++) {
    int x = j;
    service.submit(() -> {
        for (int i = 0; i <= 100; i++) {
            try {
                Thread.sleep(r.nextInt(100));
            } catch (InterruptedException e) {
            }
            all[x] = Thread.currentThread().getName() + "(" + (i + "%") + ")";
            System.out.print("\r" + Arrays.toString(all));
        }
        latch.countDown();
    });
}
latch.await();
System.out.println("\n游戏开始...");
service.shutdown();

CyclicBarrier

循环栅栏,用来进行线程协作,等待线程满足某个计数。构造时设置『计数个数』,每个线程执
行到某个需要“同步”的时刻调用 await() 方法进行等待,当等待的线程数满足『计数个数』时,继续执行

构造方法:

  • public CyclicBarrier(int parties)
  • public CyclicBarrier(int parties, Runnable barrierAction)

parties是参与线程的个数,每次调用await等待的线程数加一,直到达到parties所有线程继续运行。Runnable 参数则是可以选择计数值达到parties要做的任务。

在计数值达到parties之后,等待的线程数清零,可以重用。

CyclicBarrier cb = new CyclicBarrier(2); // 个数为2时才会继续执行
new Thread(()->{
    System.out.println("线程1开始.."+new Date());
    try {
        cb.await(); // 当个数不足时,等待
    } catch (InterruptedException | BrokenBarrierException e) {
        e.printStackTrace();
    }
    System.out.println("线程1继续向下运行..."+new Date());
}).start();
new Thread(()->{
    System.out.println("线程2开始.."+new Date());
    try { Thread.sleep(2000); } catch (InterruptedException e) { }
    try {
        cb.await(); // 2 秒后,线程个数够2,继续运行
    } catch (InterruptedException | BrokenBarrierException e) {
        e.printStackTrace();
    }
    System.out.println("线程2继续向下运行..."+new Date());
}).start();

原创文章,作者:彭晨涛,如若转载,请注明出处:https://www.codetool.top/article/juc%e5%8c%85%e4%b8%8b%e7%9a%84%e7%ba%bf%e7%a8%8b%e5%8d%8f%e4%bd%9c%e8%ae%a1%e6%95%b0countdownlatch%e5%8f%8acyclicbarrier/

(0)
彭晨涛彭晨涛管理者
上一篇 2020年2月6日
下一篇 2020年2月7日

相关推荐

  • JDK8u20字符串去重

    优点:节省大量内存 缺点:略微多占用cpu时间,新生代回收时间略微增加 -XX:+UseStringDeduplication String s1 = new String("he…

    Java 2020年1月15日
    0750
  • NIO、BIO模型对比实现文件的复制

    NIO特点 使用Channel代替Stream,是双向的 使用Selector监控多条Channel 可以在一个线程里处理多个Channel I/O Channel和Buffer …

    Java 2020年2月7日
    0180
  • Java之UDP编程

    DatagramSocket概述 上次在Java网络套接字Socket编程那篇博客里只写了Socket和ServerSocket,即TCP通信,这次来补充一下UDP通信。 和Soc…

    Java 2020年3月11日
    0810
  • try-catch-finally字节码实例探究

    本文使用Idea的jclasslib插件查看字节码。本文全程自言自语,请勿自行代入。 概述 java是怎么处理try-catch-finally的? 我们在深入理解java虚拟机第…

    2020年4月8日
    0720
  • 按键排序的Map-TreeMap源码分析

    总结 总结放前面(这篇挺短的)1. TreeMap基于红黑树实现,可以对Map中的key排序2. 它的排序和定位需要依赖比较器或实现 Comparable 接口,也因此不需要key…

    Java 2020年2月16日
    0130
  • ConcurrentHashMap源码分析

    ConcurrentHashMap的源码比较难读,特别是JDK8中几乎对其进行了重写,设计思想非常优秀,代码量也是非常多,不过我这里算是总结了一些相对重要的部分,仅供参考。 线程安…

    2020年2月13日
    0170
  • ArrayList源码分析

    总结 总结放前面防止太长不看: ArrayList内部是用数组实现的。 如果使用无参构造函数建立ArrayList,在添加第一个元素的时候会分配10个元素的空间。 ArrayLis…

    2019年11月22日
    0160
  • 深入理解java虚拟机第三版读书笔记12

    以下是第十二章 Java内存模型与线程的内容 硬件的效率与一致性 基于高速缓存的存储交互很好地解决了处理器与内存速度之间的矛盾,但是也为计算机系统带来更高的复杂度,它引入了一个新的…

    2020年1月29日
    01540
  • Java反射机制和动态代理详解

    反射 概念 反射机制: 指的是可以于运行时加载、探知、使用编译期间完全未知的类。 程序在运行状态中,可以动态加载一个只有名称的类,对于任意一个已加载的类,都能够知道这个类的所有属性…

    Java 2020年2月15日
    0580
  • ThreadPoolExecutor源码分析-线程池如何实现线程复用?

    线程的复用问题 在开始看线程池的源码之前,先来看这么一个问题: 一个Thread对象可以重复地调用start()方法吗? 试试就知道了: @Test public void tes…

    2020年5月21日
    02550

发表回复

登录后才能评论