并发编程-CyclicBarrier

62 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第42天,点击查看活动详情

概述

CyclicBarrier和CountdownLatch的作用类似,但是CountdownLatch的缺点在于不能重用,见下:

private static void test1() {
    ExecutorService service = Executors.newFixedThreadPool(5);
    for (int i = 0; i < 3; i++) {
        CountDownLatch latch = new CountDownLatch(2);
        service.submit(() -> {
            log.debug("task1 start...");
            sleep(1);
            latch.countDown();
        });
        service.submit(() -> {
            log.debug("task2 start...");
            sleep(2);
            latch.countDown();
        });
        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.debug("task1 task2 finish...");
    }
    service.shutdown();
}

想要重复使用CountdownLatch进行同步,必须创建多个CountDownLatch对象。

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

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();

注意

  • CyclicBarrier 与 CountDownLatch 的主要区别在于 CyclicBarrier 是可以重用的 >CyclicBarrier 可以被比 喻为『人满发车』

  • CountDownLatch的计数和阻塞方法是分开的两个方法,而CyclicBarrier是一个方法。

  • CyclicBarrier的构造器还有一个Runnable类型的参数,在计数为0时会执行其中的run方法。

线程安全集合类概述

线程安全集合类可以分为三大类:

  • 遗留的线程安全集合如HashtableVector

  • 使用Collections装饰的线程安全集合,如:

    • Collections.synchronizedCollection

    • Collections.synchronizedList

    • Collections.synchronizedMap

    • Collections.synchronizedSet

    • Collections.synchronizedNavigableMap

    • Collections.synchronizedNavigableSet

    • Collections.synchronizedSortedMap

    • Collections.synchronizedSortedSet

    • 说明:以上集合均采用修饰模式设计,将非线程安全的集合包装后,在调用方法时包裹了一层synchronized代码块。其并发性并不比遗留的安全集合好。

  • java.util.concurrent.*

重点介绍java.util.concurrent.* 下的线程安全集合类,可以发现它们有规律,里面包含三类关键词: Blocking、CopyOnWrite、Concurrent

  • Blocking 大部分实现基于锁,并提供用来阻塞的方法

  • CopyOnWrite 之类容器修改开销相对较重

  • Concurrent 类型的容器

    • 内部很多操作使用 cas 优化,一般可以提供较高吞吐量

    • 弱一致性

      • 遍历时弱一致性,例如,当利用迭代器遍历时,如果容器发生修改,迭代器仍然可以继续进行遍 历,这时内容是旧的

      • 求大小弱一致性,size 操作未必是 100% 准确

      • 读取弱一致性

遍历时如果发生了修改,对于非安全容器来讲,使用 fail-fast 机制也就是让遍历立刻失败,抛出 ConcurrentModificationException,不再继续遍历