J.U.C系列(二)CyclicBarrier的使用

CyclicBarrier

用来控制多个线程互相等待,只有当多个线程都到达时,这些线程才会继续执行。

和 CountdownLatch 相似,都是通过维护计数器来实现的。线程执行 await() 方法之后计数器会减 1,并进行等待,直到计数器为 0,所有调用 await() 方法而在等待的线程才能继续执行。

CyclicBarrier 和 CountdownLatch 的一个区别是,CyclicBarrier 的计数器通过调用 reset() 方法可以循环使用,所以它才叫做循环屏障。

CyclicBarrier 有两个构造函数,其中 parties 指示计数器的初始值,barrierAction 在所有线程都到达屏障的时候会执行一次。

public CyclicBarrier(int parties, Runnable barrierAction) {
    if (parties <= 0) throw new IllegalArgumentException();
    this.parties = parties;
    this.count = parties;
    this.barrierCommand = barrierAction;
}

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

demo示例

package com.leo.demo.juctest;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @ClassName: CyclicBarrierExample
 * @Description: 关于CyclicBarrier的测试
 * @Author: leo825
 * @Date: 2020-04-28 23:48
 * @Version: 1.0
 */
public class CyclicBarrierExample {

    public static void main(String[] args) {
        final int totalThread = 10;
        CyclicBarrier cyclicBarrier = new CyclicBarrier(totalThread);
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < totalThread; i++) {
            executorService.execute(() -> {
                String threadName = Thread.currentThread().getName();
                System.out.println(threadName + " 开始准备...");
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
                System.out.println(threadName + " 准备结束...");
            });
        }
        executorService.shutdown();
    }
}

执行结果

pool-1-thread-1 开始准备...
pool-1-thread-2 开始准备...
pool-1-thread-3 开始准备...
pool-1-thread-4 开始准备...
pool-1-thread-5 开始准备...
pool-1-thread-6 开始准备...
pool-1-thread-7 开始准备...
pool-1-thread-8 开始准备...
pool-1-thread-9 开始准备...
pool-1-thread-10 开始准备...
pool-1-thread-10 准备结束...
pool-1-thread-1 准备结束...
pool-1-thread-2 准备结束...
pool-1-thread-3 准备结束...
pool-1-thread-4 准备结束...
pool-1-thread-5 准备结束...
pool-1-thread-6 准备结束...
pool-1-thread-7 准备结束...
pool-1-thread-9 准备结束...
pool-1-thread-8 准备结束...

CyclicBarrier 和 CountdownLatch

区别

CyclicBarrier 和 CountdownLatch结合使用

有一个场景:男子100米短跑,需要知道每一个运动员成绩,并且打印第一名和最后一名。
具体实现如下:

package com.leo.demo.threadtest.countdownlatch;

import java.math.BigDecimal;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @ClassName: Runner
 * @Description: 设计百米赛跑统计时间的小程序
 * 1、10个选手听信号枪,统一开始跑步
 * 2、打印出来每个选手耗时时间单位秒
 * 3、打印比赛总共耗时时间单位秒
 * @Author: leo825
 * @Date: 2019-08-21 09:28
 * @Version: 1.0
 */
public class RunnerTest {

    public static void main(String[] args) throws InterruptedException, BrokenBarrierException {
        //设置10名跑步选手
        int runners = 10;
        //设置10+1个屏障,因为裁判也需要阻塞,裁判要先到终点
        CyclicBarrier cyclicBarrier = new CyclicBarrier(runners + 1);
        //设置主线程准备时间
        CountDownLatch countDownLatch = new CountDownLatch(1);
        //比赛准备时间
        long beginTime = System.currentTimeMillis();
        System.out.println("男子100米运动员就绪准备比赛");
        //使用随机数产生随机运行时间
        ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();
        //使用原子变量,模拟第一名成绩
        final AtomicInteger firstRunner = new AtomicInteger(0);
        //使用原子变量,模拟最后一名成绩
        final AtomicInteger lastRunner = new AtomicInteger(0);
        //模拟赛道上的运动员
        ExecutorService executor = Executors.newFixedThreadPool(runners);
        //运动员进入各自的跑道
        for (int i = 0; i < runners; i++) {
            //模拟百米赛跑运动员耗时
            int costTime = threadLocalRandom.nextInt(9690, 14000);
            //模拟运动员再各自跑道准备跑步
            executor.submit(() -> {
                try {
                    String threadName = Thread.currentThread().getName();
                    System.out.println(threadName + "运动员就绪!!!");
                    try {
                        //等待主线程完成的信号
                        countDownLatch.await();
                        if (firstRunner.get() == 0 || lastRunner.get() == 0) {
                            firstRunner.set(costTime);
                            lastRunner.set(costTime);
                        } else {
                            if (firstRunner.get() > costTime) {
                                firstRunner.set(costTime);
                            }
                            if (lastRunner.get() < costTime) {
                                lastRunner.set(costTime);
                            }
                        }
                        TimeUnit.MICROSECONDS.sleep(costTime);
                        System.out.println("运动员" + threadName + " 男子100米成绩为:" + transtCostTime(costTime) + "s");
                        cyclicBarrier.await();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        }
        //主线程准备时间
        long endTime = System.currentTimeMillis();
        System.out.println(Thread.currentThread().getName() + "准备时间:" + (endTime - beginTime) + "ms");

        //裁判发号施令开始比赛
        System.out.println("开始比赛...");
        countDownLatch.countDown();

        //裁判事先到终点等待,当数字到达10+1的时候开始释放锁
        cyclicBarrier.await();

        System.out.println("百米赛跑第一名成绩:" + transtCostTime(firstRunner.get()) + "s");
        System.out.println("百米赛跑最后一名成绩:" + transtCostTime(lastRunner.get()) + "s");
        executor.shutdown();
    }

    /**
     * 将毫秒转换成秒
     *
     * @param costTime
     * @return
     */
    private static BigDecimal transtCostTime(int costTime) {
        BigDecimal bigDecimal = new BigDecimal(costTime);
        bigDecimal = bigDecimal.divide(new BigDecimal(1000), 3, BigDecimal.ROUND_DOWN);
        return bigDecimal;
    }
}

打印结果如下:

男子100米运动员就绪准备比赛
main准备时间:180ms
开始比赛...
pool-1-thread-1运动员就绪!!!
pool-1-thread-2运动员就绪!!!
pool-1-thread-3运动员就绪!!!
pool-1-thread-6运动员就绪!!!
pool-1-thread-5运动员就绪!!!
pool-1-thread-4运动员就绪!!!
pool-1-thread-7运动员就绪!!!
pool-1-thread-8运动员就绪!!!
pool-1-thread-9运动员就绪!!!
pool-1-thread-10运动员就绪!!!
运动员pool-1-thread-4 男子100米成绩为:9.690s
运动员pool-1-thread-5 男子100米成绩为:10.209s
运动员pool-1-thread-2 男子100米成绩为:13.326s
运动员pool-1-thread-7 男子100米成绩为:9.692s
运动员pool-1-thread-10 男子100米成绩为:10.126s
运动员pool-1-thread-3 男子100米成绩为:10.350s
运动员pool-1-thread-8 男子100米成绩为:10.623s
运动员pool-1-thread-1 男子100米成绩为:11.263s
运动员pool-1-thread-6 男子100米成绩为:11.872s
运动员pool-1-thread-9 男子100米成绩为:11.433s
百米赛跑第一名成绩:9.690s
百米赛跑最后一名成绩:13.326s

猜你喜欢

转载自blog.csdn.net/u011047968/article/details/105827828