Java 并发编程-CountDownLatch、CyclicBarrier以及Semaphore 使用场景

CountDownLatch VS CyclicBarrier

案例分析: 100 米短跑比赛, 运动员从赛前热身准备到比赛结束裁判宣布运动员排名

public class AnswerApp {
    private static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    // 比赛选手个数
    private final static int COUNT = 8;
    // 比赛最终排名
    private static Queue<Integer> rank = new LinkedList<>();
    private static CountDownLatch countDownLatch = new CountDownLatch(COUNT);
    private static Random random = new Random();

    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(COUNT);
        ExecutorService executorService = Executors.newFixedThreadPool(COUNT);
        for (int i = 1; i <= COUNT; i++) {
            final int n = i;
            executorService.execute(() -> {
                try {
                    // 模拟选手由于个人原因需要花费部分时间进行赛前热身
                    Thread.sleep(random.nextInt(5) * 1000);

                    // 等待所有任务准备就绪
                    System.out.println(MessageFormat.format("{0} 赛道[{1}]选手已准备就绪...", LocalDateTime.now().format(DATETIME_FORMATTER), n));

                    // 选手在起跑线前准备
                    cyclicBarrier.await();

                    // 只打印一条记录即可
                    if (n == 1) {
                        System.out.println();
                        System.out.println(MessageFormat.format("{0} 准备开始啦~~~倒数 3 2 1...", LocalDateTime.now().format(DATETIME_FORMATTER)));
                        System.out.println();
                    }

                    // 模拟所有选手都已经准备完毕, 倒数 3 2 1
                    Thread.sleep(3000);

                    System.out.println(MessageFormat.format("{0} 我是赛道[{1}]选手, 我开始跑啦", LocalDateTime.now().format(DATETIME_FORMATTER), n));
                    int time = random.nextInt(10) + 5;

                    // 只打印一条记录即可
                    if (n == 1) {
                        System.out.println(MessageFormat.format("\n{0} 选手们都在疯狂奔跑中...\n", LocalDateTime.now().format(DATETIME_FORMATTER)));
                    }

                    // 模拟选手的跑完全程的耗时
                    Thread.sleep(time * 1000);

                    System.out.println(MessageFormat.format("{0} 赛道[{1}]选手达到终点, 用时[{2}]s.", LocalDateTime.now().format(DATETIME_FORMATTER), n, time));

                    rank.add(n);
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                    System.out.println(MessageFormat.format("{0} 选手[{1}]退出了比赛, 退赛原因[{2}]", LocalDateTime.now().format(DATETIME_FORMATTER), n, e.getMessage()));
                } finally {
                    countDownLatch.countDown(); // 到达终点计数器减1
                }
            });
        }

        try {
            // 阻塞, 直到所有线程都执行完毕, 即所有选手都跑完比赛(包括退赛)
            countDownLatch.await();

            System.out.println();
            System.out.println(MessageFormat.format("{0} 最终排名[{1}]", LocalDateTime.now().format(DATETIME_FORMATTER), rank));
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 关闭线程池
            executorService.shutdown();
        }
    }
}
  • CountDownLatch: 用于记录选手达到终点的计数器, 计数器为0时, 所有选手都结束比赛(跑完比赛和退赛)
  • CyclicBarrier: 用于所有选手在起跑线相互等待, 等待所有选手都准备就绪, 再进行比赛[有重置功能, 调用reset()方法]

程序运行结果

2019-07-06 11:46:18 赛道[2]选手已准备就绪...
2019-07-06 11:46:18 赛道[3]选手已准备就绪...
2019-07-06 11:46:19 赛道[4]选手已准备就绪...
2019-07-06 11:46:19 赛道[6]选手已准备就绪...
2019-07-06 11:46:20 赛道[7]选手已准备就绪...
2019-07-06 11:46:21 赛道[1]选手已准备就绪...
2019-07-06 11:46:21 赛道[5]选手已准备就绪...
2019-07-06 11:46:21 赛道[8]选手已准备就绪...

2019-07-06 11:46:21 准备开始啦~~~倒数 3 2 1...

2019-07-06 11:46:24 我是赛道[8]选手, 我开始跑啦
2019-07-06 11:46:24 我是赛道[2]选手, 我开始跑啦
2019-07-06 11:46:24 我是赛道[6]选手, 我开始跑啦
2019-07-06 11:46:24 我是赛道[3]选手, 我开始跑啦
2019-07-06 11:46:24 我是赛道[4]选手, 我开始跑啦
2019-07-06 11:46:24 我是赛道[7]选手, 我开始跑啦
2019-07-06 11:46:24 我是赛道[5]选手, 我开始跑啦
2019-07-06 11:46:24 我是赛道[1]选手, 我开始跑啦

2019-07-06 11:46:24 选手们都在疯狂奔跑中...

2019-07-06 11:46:31 赛道[1]选手达到终点, 用时[7]s.
2019-07-06 11:46:32 赛道[4]选手达到终点, 用时[8]s.
2019-07-06 11:46:32 赛道[5]选手达到终点, 用时[8]s.
2019-07-06 11:46:34 赛道[3]选手达到终点, 用时[10]s.
2019-07-06 11:46:37 赛道[2]选手达到终点, 用时[13]s.
2019-07-06 11:46:37 赛道[7]选手达到终点, 用时[13]s.
2019-07-06 11:46:38 赛道[8]选手达到终点, 用时[14]s.
2019-07-06 11:46:38 赛道[6]选手达到终点, 用时[14]s.

2019-07-06 11:46:38 最终排名[[1, 4, 5, 3, 2, 7, 8, 6]]

 

Semaphore

案例分析: 一个工厂有 n-MACHINE 台机器, 但是有 n-WORKER 个工人, 一台机器同时只能被一个工人使用, 只有使用完了,其他工人才能继续使用

public class AnswerApp {
    /** 工人数量 */
    private static final int WORKER = 10;
    /** 机器数量 */
    private static final int MACHINE = 3;

    private static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    public static void main(String[] args) {
        final Semaphore semaphore = new Semaphore(MACHINE);
        ExecutorService executorService = Executors.newCachedThreadPool();

        for (int i = 1; i <= WORKER; i++) {
            final int index = i;
            executorService.execute(() -> {
                try {
                    // 工人获取一台机器用于作业的许可
                    semaphore.acquire();

                    System.out.println(MessageFormat.format("{0} 工作人员[{1}] take up a machine.",
                            LocalDateTime.now().format(DATETIME_FORMATTER), index));

                    Thread.sleep(5000);

                    System.out.println(MessageFormat.format("{0} 工作人员[{1}] release a machine.",
                            LocalDateTime.now().format(DATETIME_FORMATTER), index));

                    // 工厂生产完毕, 释放机器
                    semaphore.release();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }

        executorService.shutdown();
    }
}

程序运行结果

2019-07-06 13:42:40 工作人员[2] take up a machine.
2019-07-06 13:42:40 工作人员[1] take up a machine.
2019-07-06 13:42:40 工作人员[3] take up a machine.
2019-07-06 13:42:45 工作人员[3] release a machine.
2019-07-06 13:42:45 工作人员[2] release a machine.
2019-07-06 13:42:45 工作人员[1] release a machine.
2019-07-06 13:42:45 工作人员[4] take up a machine.
2019-07-06 13:42:45 工作人员[7] take up a machine.
2019-07-06 13:42:45 工作人员[5] take up a machine.
2019-07-06 13:42:50 工作人员[4] release a machine.
2019-07-06 13:42:50 工作人员[7] release a machine.
2019-07-06 13:42:50 工作人员[6] take up a machine.
2019-07-06 13:42:50 工作人员[8] take up a machine.
2019-07-06 13:42:50 工作人员[5] release a machine.
2019-07-06 13:42:50 工作人员[9] take up a machine.
2019-07-06 13:42:55 工作人员[8] release a machine.
2019-07-06 13:42:55 工作人员[9] release a machine.
2019-07-06 13:42:55 工作人员[6] release a machine.
2019-07-06 13:42:55 工作人员[10] take up a machine.
2019-07-06 13:43:00 工作人员[10] release a machine.

 

总结

  • CountDownLatch: 一般用于某个线程A等待若干个其他线程执行完任务之后,它再执行

    • 裁判等待所有的选手都跑完再进行公布排名
  • CyclicBarrier: 一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行

    • 起跑准备时, 所有选手都相互等待, 等待所有人都准备好再一起开跑
  • CountDownLatch是不能够重用的,而CyclicBarrier是可以重用的(通过调用 reset 方法)

  • Semaphore: 其实和锁有点类似,它一般用于控制对某组资源的访问权限

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jaemon

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值