CyclicBarrier的简单使用

走远了吗. 提交于 2020-01-25 15:47:43

通过之前的这篇博客CountDownLatch的简单应用和实现原理,可以了解到,CountDownLatch实质上做的事,就是让一个线程等待多个线程

通过CountDownLatch我们可以对一些没有前后依赖的方法做到并行执行,节省大量的时间,但是这时候出现了新的需求:

  • 现在我要做的是一个循环的操作,反复的从数据库里取出订单数据和快递单数据,然后这两个数据要进行对比,我要知道哪些订单配送失败了

思路一:在循环里反复新建CountDownLatch类,大致代码如下:

 public static void main(String[] args) throws InterruptedException {
        if (true) {
            for (; ; ) {
                CountDownLatch countDownLatch = new CountDownLatch(2);
                Thread thread1 = new Thread(() -> {
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("获取订单数据");
                    countDownLatch.countDown();
                });
                Thread thread2 = new Thread(() -> {
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("获取快递单数据");
                    countDownLatch.countDown();
                });
                thread1.start();
                thread2.start();
                countDownLatch.await();
                System.out.println("对比订单数据和快递单数据后插入数据库");
            }
        }
    }

运行效果如下:
在这里插入图片描述
可以看到使用CountDownLatch类是可以实现的,一板一眼的按照订单数据----->
快递单数据----->对比数据入库这样的顺序执行,那么这种方式还能优化么?
答案是可以的。


首先优化我们自己的业务逻辑和想法,要知道在这个循环里,我们完全没有必要等到数据对比入库完之后再重新开始获取订单和快递单数据,我们完全可以把对比数据入库这步操作和重新开始获取订单和快递单数据并行操作,所以这时候就要有3个线程,获取订单线程获取快递单线程对比数据入库线程获取订单线程获取快递单线程两者需要互相等待,都执行完毕后再调用对比数据入库线程。这个逻辑可以用CyclicBarrier类来实现。

什么是CyclicBarrier

顾名思义,CyclicBarrier就是是回环屏障。它可以一组线程都要达到某种状态(也可以理解为屏障)后才会一起执行。同时当所有线程都执行完毕,CyclicBarrier会进行状态的重置,方便复用,这就是回环。总的来说CyclicBarrier就是个可以循环利用的屏障。不像CountDownLatch只能用一次。

CyclicBarrier的简单使用

public static void main(String[] args) throws Exception {
        //构建CyclicBarrier类,并加上回调函数
        CyclicBarrier cyclicBarrier = new CyclicBarrier(2, new Runnable() {
            @Override
            public void run() {
              try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("对比订单数据和快递单数据后插入数据库");
            }
        });

        //订单线程
        Thread thread1 = new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) {
                    Thread.sleep(2000);
                    System.out.println("获取订单数据");
                    cyclicBarrier.await();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        });

        //快递单线程
        Thread thread2 = new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) {
                    Thread.sleep(2000);
                    System.out.println("获取快递单数据");
                    cyclicBarrier.await();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        thread1.start();
        thread2.start();
    }

以上我们调用了 CyclicBarrier类带有回调方法的构造函数


//parties--需要让多少个线程或者任务等待至barrier状态
//barrierAction 回调方法,由最后一个到达屏障的线程执行
public CyclicBarrier(int parties, Runnable barrierAction) {}

运行结果如下:
在这里插入图片描述
发现其实和使用CountDownLatch一样,也没差别,这是因为回调方法是同步的,所以我们再加个线程,让回调方法异步执行,能够成功,就代表之前的逻辑实现了。

修改后代码如下:

public static void main(String[] args) throws Exception {

        //创建线程池
        Executor executor = Executors.newFixedThreadPool(1);
        //构建CyclicBarrier类,并加上回调函数,且异步调用
        CyclicBarrier cyclicBarrier = new CyclicBarrier(2, () -> {
            executor.execute(() -> {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("对比订单数据和快递单数据后插入数据库");
                    }
            );
        });
        //订单线程
        Thread thread1 = new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) {
                    Thread.sleep(2000);
                    System.out.println("获取订单数据");
                    cyclicBarrier.await();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        //快递单线程
        Thread thread2 = new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) {
                    Thread.sleep(2000);
                    System.out.println("获取快递单数据");
                    cyclicBarrier.await();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        thread1.start();
        thread2.start();
    }

运行结果如下:
在这里插入图片描述
从结果可以看出,订单线程和快递线程两两成对等待,和对比数据线程并行执行

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!