Java并发包学习(CountDownLatch,Seamphore,CyclicBarrier,Exchanger)

我们常说的“并发包”指的是java.util.concurrent这个包,后面简称 J.U.C,里面包含大量多线程和并发编程的工具。J.U.C 包是 JDK 1.5 版本引入,本文所有内容基于 JDK11。

工具类

工具包内主要有这几个工具:计数器CountDownLatch、回环栅栏(光看名字估计一头雾水)CyclicBarrier、信号量Semaphore、创建线程池的Executors,最后一个交换数据的Exchanger。本章几个工具的原理都是基于 AQS,关于 AQS 和ReentrantLock,都有单独的文章解读,就不专门介绍了。

CountDownLatch

一个计数器,常见这样一种场景:多个任务分发个不同的线程去执行,全部执行完毕后回到主线程。当然有不同的实现方式,用CountDownLatch实现起来就很简单:

int taskNum = 20;
        CountDownLatch countLatch = new CountDownLatch(taskNum);

        for (int i = 0; i < taskNum; i++) {
            new Thread(() -> {
                try {
                    Thread.sleep(200); //假装做了点什么
                    System.out.println(Thread.currentThread().getName() + "执行完毕");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                countLatch.countDown();  // 减1操作
            }).start();
        }

        try {
            countLatch.await(); // 阻塞线程,直至countDown方法调用次数等于taskNum,才会释放线程。for循环次数必须大于等于taskNum
            System.out.println("全部任务执行完毕");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
Seamphore

信号量,类似于控制并发的时候用到的“令牌桶”算法,通过控制信号总数,不断释放和回收信号来控制并发数量。假设有这么一个场景,假设我们有五个通道可以执行任务,任务总数是 40,所以同一时刻只能有最多五个线程执行,其余的要等待,因此我们使用Seamphore来不断方法许可和收回许可:

Semaphore semaphore = new Semaphore(5);

        for (int i = 0; i < 40; i++) {
            new Thread(() -> {
                try {
                    semaphore.acquire();  // 获取许可,获取失败则进入阻塞
                    System.out.println(Thread.currentThread().getName() + "取得许可,开始执行任务");
                    Thread.sleep(new Random().nextInt(2000));
                    System.out.println(Thread.currentThread().getName() + "任务完成,释放许可");
                    semaphore.release();  // 释放许可。释放后激活阻塞线程去争取许可
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
CyclicBarrier

回环栅栏光看名字不好理解,其实作用很上面的计数器类似,有点类似于 Java 虚拟机中的“安全点”。执行一个任务的时候,所有线程到达栅栏之后停止,等待所有其他线程都到达这个点,然后一起进入下一阶段。与计数器不同的是,CyclicBarrier可以重复使用,举个栗子:
用于协调多个线程同步执行操作的场合,所有线程等待完成,然后一起做事情( 相互之间都准备好,然后一起做事情 )
例如百米赛跑,必须等待所有运动员都准备好了,才能比赛。

CyclicBarrier cyclicBarrier = new CyclicBarrier(20);

        for (int i = 0; i < 20; i++) {
            new Thread(() -> {
                long timeStamp = System.currentTimeMillis();
                try {
                    Thread.sleep(new Random().nextInt(2000));
                    System.out.println(Thread.currentThread().getName() + ":一阶段任务完成,花费了" + (System.currentTimeMillis() - timeStamp) + "毫秒,开始等待其他线程");
                    cyclicBarrier.await();
                    System.out.println(Thread.currentThread().getName() + ":所有线程执行完成,开始下一阶段");
                    timeStamp = System.currentTimeMillis();
                    Thread.sleep(new Random().nextInt(2000));
                    System.out.println(Thread.currentThread().getName() + ":二阶段任务完成,花费了" + (System.currentTimeMillis() - timeStamp) + "毫秒,开始等待其他线程");
                    cyclicBarrier.await();
                    System.out.println(Thread.currentThread().getName() + ":所有线程任务完成");
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
Exchanger

交换器顾名思义,就是用来交换数据,理解和使用起来是最简单的,但是内部实现很精巧复杂。使用很简单,只有两个方法,作用就是两个线程在一个安全点交换数据,产生数据慢的那个会阻塞等待。

Exchanger<Integer> exchanger = new Exchanger<>();

        new Thread(() -> {
            int num = new Random().nextInt(1000);
            System.out.println("交换之前:Thread1:" + num);
            try {
                num = exchanger.exchange(num);
                System.out.println("交换完毕:Thread1:" + num);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        new Thread(() -> {
            int num = new Random().nextInt(1000);
            System.out.println("交换之前:Thread2:" + num);
            try {
                Thread.sleep(2000);
                num = exchanger.exchange(num);
                System.out.println("交换完毕:Thread2:" + num);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值