CyclicBarrier(同步屏障)的简单使用

CyclicBarrer简介

转载自:https://blog.csdn.net/fanrenxiang/article/details/79916778
CyclicBarrer,可循环使用的屏障,功能是让多个线程到达某个点时被阻塞,直到最后一个线程达到这个屏障便释放所有线程,和CountDownLatch的区别即在于线程释放后屏障是否可重用。

实例化:通过带参数的new CyclicBarrer(N)可实例化CyclicBarrier,N代表需要屏障拦截(阻塞)的线程数,也可以使用new CyclicBarrier(N,Runnable)的方式指定当所有阻塞的线程都到达屏障点后优先执行的任务barrierAction。

public CyclicBarrier(int parties) {……}
public CyclicBarrier(int parties, Runnable barrierAction) {……}

阻塞线程:通过调用await方法告诉当前线程已到达屏障,进入阻塞等待状态。也可以指定阻塞时间await(timeout,unit),防止阻塞时间过长,当阻塞超过指定时间,抛出TimeoutException

public int await() throws InterruptedException, BrokenBarrierException {……}
public int await(long timeout, TimeUnit unit) {……}

测试demo:以 三个线程计算任务为例,其中一个线程计算时间很长,于是调用await(time,unit)来指定等待时间

public class CyclicBarrierService {
    static ExecutorService executorService = new ThreadPoolExecutor(3, 3, 30L, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(15));
    static CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
 
    public static void statistic() throws Exception {
        Future<Integer> task1 = executorService.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                System.out.println("我是任务一");
                cyclicBarrier.await(1, TimeUnit.SECONDS);
                return 1;
            }
        });
        Future<Integer> task2 = executorService.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                System.out.println("我是任务二");
                cyclicBarrier.await(1, TimeUnit.SECONDS);
                return 2;
            }
        });
        Future<Integer> task3 = executorService.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                System.out.println("我是任务三");
                Thread.sleep(5000);   //模拟任务3要执行很长时间
                cyclicBarrier.await(1, TimeUnit.SECONDS);
                return 3;
            }
        });
 
        int result1 = task1.get();
        int result2 =  task2.get();
        int result3 = task3.get();
        System.out.println("多线程计算结果为");
        System.out.println(result1 + result2 + result3);
        executorService.shutdown();
    }
    public static void main(String[] args) throws Exception {
        statistic();
    }
}

因为我在获取线程计算结果时候未使用FutureTask.isDone()来判断当前任务是否计算完成(直接调用FutureTask.get()可能会阻塞,加了isDone判断,由于子线程任务还被阻塞在屏障点,所以获取不到计算结果),上述代码就抛出超时异常

我是任务一
我是任务二
我是任务三
Exception in thread "main" java.util.concurrent.ExecutionException: java.util.concurrent.TimeoutException
	at java.util.concurrent.FutureTask.report(FutureTask.java:122)
	at java.util.concurrent.FutureTask.get(FutureTask.java:188)
	at com.pptv.activityapi.controller.actmodule.CyclicBarrierService.statistic(CyclicBarrierService.java:49)
	at com.pptv.activityapi.controller.actmodule.CyclicBarrierService.main(CyclicBarrierService.java:62)
Caused by: java.util.concurrent.TimeoutException
	at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
	at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:427)
	at com.pptv.activityapi.controller.actmodule.CyclicBarrierService$1.call(CyclicBarrierService.java:25)
	at com.pptv.activityapi.controller.actmodule.CyclicBarrierService$1.call(CyclicBarrierService.java:21)
	at java.util.concurrent.FutureTask.run(FutureTask.java:262)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
	at java.lang.Thread.run(Thread.java:745)

重置计数器N:reset方法将屏障重置为其初始状态。 如果任何一方当前正在屏障等待,他们将返回BrokenBarrierException。

测试demo:

public class CyclicBarrierService {
    static ExecutorService executorService = new ThreadPoolExecutor(3, 3, 30L, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(15));
    static CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
 
    public static void statistic() {
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    cyclicBarrier.await();
                    log.info("我是任务一……");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    cyclicBarrier.await();
                    log.info("我是任务二……");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    cyclicBarrier.reset();
                    log.info("我是任务三……");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
        executorService.shutdown();
    }
 
    public static void main(String[] args) {
        statistic();
    }
}

第三个线程调用CyclicBarrier.reset()方法后将导致前面两个阻塞在屏障点的线程显式的抛出java.util.concurrent.BrokenBarrierException,输出结果如下

java.util.concurrent.BrokenBarrierException
	at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:243)
	at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:355)
	at com.pptv.activityapi.controller.actmodule.CyclicBarrierService$1.run(CylicBarrierService.java:25)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
	at java.util.concurrent.FutureTask.run(FutureTask.java:262)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
	at java.lang.Thread.run(Thread.java:745)
java.util.concurrent.BrokenBarrierException
	at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:243)
	at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:355)
	at com.pptv.activityapi.controller.actmodule.CyclicBarrierService$2.run(CylicBarrierService.java:37)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
	at java.util.concurrent.FutureTask.run(FutureTask.java:262)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
	at java.lang.Thread.run(Thread.java:745)
2018-11-14 15:11:39,535[com.pptv.activityapi.controller.actmodule.CyclicBarrierService][INFO]我是任务三……

CyclicBarrier使用场景及demo
结合上面说的,CyclicBarrier非常适合多线程计算任务,功能还是和CountDownLatch一致的,分组执行任务,最后汇总结果

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.concurrent.*;
 
@Slf4j
@Service
public class CyclicBarrierService {
    static ExecutorService executorService = new ThreadPoolExecutor(3, 3, 30L, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(15));
    static CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
    
    public static void statistic() throws Exception {
        Future<Integer> task1 = executorService.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                log.info("我是任务一");
                cyclicBarrier.await(1, TimeUnit.SECONDS);
                return 1;
            }
        });
        Future<Integer> task2 = executorService.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                log.info("我是任务二");
                cyclicBarrier.await(1, TimeUnit.SECONDS);
                return 2;
            }
        });
        Future<Integer> task3 = executorService.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                log.info("我是任务三");
                cyclicBarrier.await(1, TimeUnit.SECONDS);
                return 3;
            }
        });
        int result1 =  task1.get();
        int result2 =  task2.get();
        int result3 =  task3.get();
        log.info("多线程计算结果为");
        log.info(String.valueOf(result1 + result2 + result3));
        executorService.shutdown();
    }
 
    public static void main(String[] args) throws Exception {
        statistic();
    }
}

输出结果为

2018-11-14 16:39:24,127[com.pptv.activityapi.controller.actmodule.CyclicBarrierService][INFO]我是任务一
2018-11-14 16:39:24,127[com.pptv.activityapi.controller.actmodule.CyclicBarrierService][INFO]我是任务二
2018-11-14 16:39:24,127[com.pptv.activityapi.controller.actmodule.CyclicBarrierService][INFO]我是任务三
2018-11-14 16:39:24,128[com.pptv.activityapi.controller.actmodule.CyclicBarrierService][INFO]多线程计算结果为
2018-11-14 16:39:24,128[com.pptv.activityapi.controller.actmodule.CyclicBarrierService][INFO]6

总结

这个知识点是我以前没有接触过的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值