• 《Java并发编程的艺术》读书笔记 - 第八章 - Java中的并发工具类


    目录

    前言

    等待多线程完成的 CountDownLatch

    示例

    同步屏障 CyclicBarrier

    示例

    CyclicBarrier 和 CountDownLatch 的区别

    控制并发线程数量的 Semaphore

    示例

    线程间交换数据的 Exchanger

    示例


    前言

    在JDK的并发包里提供了几个非常有用的并发工具类。CountDownLatch、CyclicBarrier 和 Semaphore 工具类提供了一种并发流程控制的手段,Exchanger 工具类则提供了在线程间交换数据的一种手段。

    等待多线程完成的 CountDownLatch

    CountDownLatch 允许一个或多个线程等待其他线程完成的操作。 

    示例

    集齐 5 个不同的勋章可以解锁成就 - “勋章达人” 

    1. public class CountDownLatchDemo {
    2. static CountDownLatch medalCount = new CountDownLatch(5);
    3. public static void main(String[] args) throws InterruptedException {
    4. getMedal();
    5. // 等待所有勋章集齐
    6. medalCount.await();
    7. System.out.println("解锁新成就 - 勋章达人");
    8. }
    9. private static void getMedal() throws InterruptedException {
    10. for (int i = 1; i <= 5; i++) {
    11. new Thread(() -> {
    12. System.out.println("集齐 " + Thread.currentThread().getName() + " 号勋章...");
    13. medalCount.countDown();
    14. }, String.valueOf(i)).start();
    15. }
    16. }
    17. }


    同步屏障 CyclicBarrier

    CyclicBarrier 的字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续运行。 

    示例

    模拟百米赛跑比赛现场,裁判员等待每组选手准备好后鸣枪开赛。共2组选手,每组3人。

    1. public class CyclicBarrierDemo {
    2. static CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
    3. public static void main(String[] args) throws InterruptedException {
    4. raceGame();
    5. }
    6. private static void raceGame() throws InterruptedException {
    7. for (int i = 1; i <= 2; i++) {
    8. System.out.println("第 " + i + " 轮准备开始...");
    9. for (int j = 1; j <= 3; j++) {
    10. new Thread(() -> {
    11. System.out.println("第 " + Thread.currentThread().getName() + " 号选手已经准备好了...");
    12. try {
    13. // 等待其它选手准备
    14. cyclicBarrier.await();
    15. } catch (Exception e) {
    16. e.printStackTrace();
    17. }
    18. }, String.valueOf(j)).start();
    19. }
    20. Thread.sleep(100);
    21. // 比赛结束重置
    22. cyclicBarrier.reset();
    23. System.out.println("第 " + i + " 轮结束...");
    24. }
    25. }
    26. }

    CyclicBarrier 和 CountDownLatch 的区别

    CountDownLatch 的计数器只能使用一次,而 CyclicBarrier 的计数器可以使用 reset() 方法重置。所以 CyclicBarrier 能处理更为复杂的业务场景。例如,如果计算发生错误,可以重置计数器,并让线程重新执行一次。 


    控制并发线程数量的 Semaphore

    Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源。 

    示例

    有 6 辆车在一个只有 2 个停车位的停车场抢车位,模拟随机进出顺序。

    1. public class SemaphoreDemo {
    2. static Semaphore semaphore = new Semaphore(2);
    3. public static void main(String[] args) {
    4. parkingGame();
    5. }
    6. private static void parkingGame() {
    7. for (int i = 0; i < 6; i++) {
    8. new Thread(() -> {
    9. try {
    10. semaphore.acquire();
    11. System.out.println(Thread.currentThread().getName() + " 号车抢到了车位");
    12. Thread.sleep(200);
    13. System.out.println(Thread.currentThread().getName() + " 号车释放了车位");
    14. semaphore.release();
    15. } catch (InterruptedException e) {
    16. e.printStackTrace();
    17. }
    18. }, String.valueOf(i + 1)).start();
    19. }
    20. }
    21. }


    线程间交换数据的 Exchanger

    Exchanger(交换者)是一个用于线程间协作的工具类。Exchanger 用于进行线程间的数据交换。它提供一个同步点,在这个同步点,两个线程可以交换彼此的数据。这两个线程通过 exchange 方法交换数据,如果第一个线程先执行 exchange() 方法,它会一直等待第二个线程也执行 exchange 方法,当两个线程都到达同步点时,这两个线程就可以交换数据,将本线程生产出来的数据传递给对方。

    示例

    A、B 两人分别统计公司流水后进行对账,为了避免流水错误。

    1. public class ExchangerDemo {
    2. static Exchanger exchanger = new Exchanger<>();
    3. public static void main(String[] args) {
    4. reconciliationGame();
    5. }
    6. private static void reconciliationGame() {
    7. new Thread(() -> {
    8. Integer moneyA = 500 * 10000;
    9. try {
    10. Integer exchangeB = exchanger.exchange(moneyA);
    11. System.out.println("A 与 B 是否一致: " + exchangeB.equals(moneyA) + " A: " + moneyA + " B: " + exchangeB);
    12. } catch (InterruptedException e) {
    13. e.printStackTrace();
    14. }
    15. }, "A").start();
    16. new Thread(() -> {
    17. Integer moneyB = 500 * 10000;
    18. try {
    19. Integer exchangeA = exchanger.exchange(moneyB);
    20. System.out.println("A 与 B 是否一致: " + moneyB.equals(exchangeA) + " A: " + exchangeA + " B: " + moneyB);
    21. } catch (InterruptedException e) {
    22. e.printStackTrace();
    23. }
    24. }, "B").start();
    25. }
    26. }

    若将 A 流水修改为与 B 不一致,结果如下:

  • 相关阅读:
    【Spring AOP】暴力打通两个切面之间的通信
    python字符与字典、列表相互转换
    Vue中的计算属性和侦听器有什么区别?
    【Hadoop】二、Hadoop MapReduce与Hadoop YARN
    从官方文档中探索 Android App 架构演进的方向
    #422 编码人声:行业大会的幕后故事
    three.js r146动态加载fbx文件,非module模式
    图书管理系统SQL语句
    视频理解论文实验笔记2014-2022
    系统性能分析工具
  • 原文地址:https://blog.csdn.net/shuttlepro/article/details/127820391