【小家java】JUC并发编程工具之CountDownLatch(闭锁)、CyclicBarrier、Semaphore的使用(中)

简介: 【小家java】JUC并发编程工具之CountDownLatch(闭锁)、CyclicBarrier、Semaphore的使用(中)

一家人一起吃饭代码示例:


先定义一些方法,模拟吃饭场景


    public static void fatherToRes() {
        System.out.println("爸爸步行去饭店需要3小时。");
    }
    public static void motherToRes() {
        System.out.println("妈妈挤公交去饭店需要2小时。");
    }
    public static void meToRes() {
        System.out.println("我乘地铁去饭店需要1小时。");
    }
    public static void togetherToEat() {
        System.out.println("一家人到齐了,开始吃饭");
    }


顺序执行:


    public static void main(String[] args) {
        fatherToRes();
        motherToRes();
        meToRes();
        togetherToEat();
    }
输出:
爸爸步行去饭店需要3小时。
妈妈挤公交去饭店需要2小时。
我乘地铁去饭店需要1小时。
一家人到齐了,开始吃饭


我们发现,光集合就花了6个小时。改进版本:


    public static void main(String[] args) {
        new Thread(() -> fatherToRes()).start();
        new Thread(() -> motherToRes()).start();
        new Thread(() -> meToRes()).start();
        togetherToEat();
    }
输出:
爸爸步行去饭店需要3小时。
一家人到齐了,开始吃饭
妈妈挤公交去饭店需要2小时。
我乘地铁去饭店需要1小时。


这个好像也不行,人还没到齐就开饭了。继续改进


    //定义一个变量 必须等于0了才开饭
    private static volatile int i = 3;
    public static void main(String[] args) {
        new Thread(() -> {
            fatherToRes();
            i--;
        }).start();
        new Thread(() -> {
            motherToRes();
            i--;
        }).start();
        new Thread(() -> {
            meToRes();
            i--;
        }).start();
        while (i != 0) {
            //此处一直hole住等待
        }
        togetherToEat();
    }
输出:
爸爸步行去饭店需要3小时。
妈妈挤公交去饭店需要2小时。
我乘地铁去饭店需要1小时。
一家人到齐了,开始吃饭


这个实际上达到了效果。但是,但是while盲等待是对于CPU的消耗太巨大了,我们需要更好的实现方式。(备注:此处用volatile修饰i是有并发问题的,读者可以使用AtomicInteger或者LongAdder改进,此处我就不改了哈)


可以参考:

【小家java】使用volatile关键字来实现内存可见性、实现轻量级锁

【小家java】AtomicLong可以抛弃了,请使用LongAdder代替(或使用LongAccumulator)


最终版本:


    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> {
            fatherToRes();
            latch.countDown();
        }).start();
        new Thread(() -> {
            motherToRes();
            latch.countDown();
        }).start();
        new Thread(() -> {
            meToRes();
            latch.countDown();
        }).start();
        latch.await();
        togetherToEat();
    }
输出:
妈妈挤公交去饭店需要2小时。
爸爸步行去饭店需要3小时。
我乘地铁去饭店需要1小时。
一家人到齐了,开始吃饭


这样子,我们就不用一直hold住cpu,不用盲等了。

CyclicBarrier


CyclicBarrier概念,前言里面已经有所介绍了。CyclicBarrier默认的构造方法是CyclicBarrier(int parties),其参数表示屏障拦截的线程数量,每个线程调用await方法告诉CyclicBarrier我已经到达了屏障,然后当前线程被阻塞。


   public CyclicBarrier(int parties) {
        this(parties, null);
    }


我们把上面一家人一起吃饭的例子改造一下:


    private static CyclicBarrier cyclicBarrier = new CyclicBarrier(4);
    public static void main(String[] args) throws Exception {
        new Thread(() -> {
            fatherToRes();
            try {
                cyclicBarrier.await();
            } catch (Exception e) {
            }
        }).start();
        new Thread(() -> {
            motherToRes();
            try {
                cyclicBarrier.await();
            } catch (Exception e) {
            }
        }).start();
        new Thread(() -> {
            meToRes();
            try {
                cyclicBarrier.await();
            } catch (Exception e) {
            }
        }).start();
        //主线程也要await
        cyclicBarrier.await();
        togetherToEat();
    }


需要注意的是:

1、main主线程也是一个线程,所以也要await

2、new CyclicBarrier的值是4,而不是3(会有个线程控制不了),也不是5(程序将永远等待,因为没有第五个线程执行await方法,即没有第五个线程到达屏障,所以之前到达屏障的四个线程都不会继续执行。)


CyclicBarrier还提供一个更高级的构造函数CyclicBarrier(int parties, Runnable barrierAction),用于在线程到达屏障时(前提条件也必须是先达到屏障),优先执行barrierAction,方便处理更复杂的业务场景。(比如一家人吃饭,必须永远是爸爸先吃)

private static CyclicBarrier cyclicBarrier = new CyclicBarrier(4, () -> System.out.println("人到齐了,爸爸先动筷子"));
    public static void main(String[] args) throws Exception {
        new Thread(() -> {
            fatherToRes();
            try {
                cyclicBarrier.await();
            } catch (Exception e) {
            }
        }).start();
        new Thread(() -> {
            motherToRes();
            try {
                cyclicBarrier.await();
            } catch (Exception e) {
            }
        }).start();
        new Thread(() -> {
            meToRes();
            try {
                cyclicBarrier.await();
            } catch (Exception e) {
            }
        }).start();
        //主线程也要await
        cyclicBarrier.await();
        togetherToEat();
    }
输出:
爸爸步行去饭店需要3小时。
妈妈挤公交去饭店需要2小时。
我乘地铁去饭店需要1小时。
人到齐了,爸爸先动筷子
一家人到齐了,开始吃饭


应用场景


CyclicBarrier可以用于多线程计算数据,最后合并计算结果的应用场景。比如我们用一个Excel保存了用户所有银行流水,每个Sheet保存一个帐户近一年的每笔银行流水,现在需要统计用户的日均银行流水,先用多线程处理每个sheet里的银行流水,都执行完之后,得到每个sheet的日均银行流水,最后,再用barrierAction用这些线程的计算结果,计算出整个Excel的日均银行流水。

相关文章
|
13天前
|
Java
JAVA并发编程系列(7)Semaphore信号量剖析
腾讯T2面试,要求在3分钟内用不超过20行代码模拟地铁安检进站过程。题目设定10个安检口,100人排队,每人安检需5秒。实际中,这种题目主要考察并发编程能力,特别是多个线程如何共享有限资源。今天我们使用信号量(Semaphore)实现,限制同时进站的人数,并通过信号量控制排队和进站流程。并详细剖析信号量核心原理和源码。
|
11天前
|
算法 Java
JAVA并发编程系列(8)CountDownLatch核心原理
面试中的编程题目“模拟拼团”,我们通过使用CountDownLatch来实现多线程条件下的拼团逻辑。此外,深入解析了CountDownLatch的核心原理及其内部实现机制,特别是`await()`方法的具体工作流程。通过详细分析源码与内部结构,帮助读者更好地理解并发编程的关键概念。
|
5天前
|
Java
java小工具util系列4:基础工具代码(Msg、PageResult、Response、常量、枚举)
java小工具util系列4:基础工具代码(Msg、PageResult、Response、常量、枚举)
17 5
|
4天前
|
Java 数据库
java小工具util系列1:日期和字符串转换工具
java小工具util系列1:日期和字符串转换工具
12 3
|
5天前
|
Java
java小工具util系列5:java文件相关操作工具,包括读取服务器路径下文件,删除文件及子文件,删除文件夹等方法
java小工具util系列5:java文件相关操作工具,包括读取服务器路径下文件,删除文件及子文件,删除文件夹等方法
14 4
|
10天前
|
Java
JAVA并发编程系列(9)CyclicBarrier循环屏障原理分析
本文介绍了拼多多面试中的模拟拼团问题,通过使用 `CyclicBarrier` 实现了多人拼团成功后提交订单并支付的功能。与之前的 `CountDownLatch` 方法不同,`CyclicBarrier` 能够确保所有线程到达屏障点后继续执行,并且屏障可重复使用。文章详细解析了 `CyclicBarrier` 的核心原理及使用方法,并通过代码示例展示了其工作流程。最后,文章还提供了 `CyclicBarrier` 的源码分析,帮助读者深入理解其实现机制。
|
3天前
|
SQL 安全 Java
JAVA代码审计SAST工具使用与漏洞特征
JAVA代码审计SAST工具使用与漏洞特征
16 1
|
4天前
|
SQL Java 索引
java小工具util系列2:字符串工具
java小工具util系列2:字符串工具
7 2
|
6天前
|
JSON Java fastjson
java小工具util系列3:JSON和实体类转换工具
java小工具util系列3:JSON和实体类转换工具
11 2
|
29天前
|
Arthas Java 测试技术
Java字节码文件、组成,jclasslib插件、阿里arthas工具,Java注解
Java字节码文件、组成、详解、分析;常用工具,jclasslib插件、阿里arthas工具;如何定位线上问题;Java注解
Java字节码文件、组成,jclasslib插件、阿里arthas工具,Java注解
下一篇
无影云桌面