• CyclicBarrier的使用


    最近一直整并发这块东西,顺便写点Java并发的例子,给大家做个分享,也强化下自己记忆,如果有什么错误或者不当的地方,欢迎大家斧正。

    CyclicBarrier是一种多线程并发控制实用工具,和CountDownLatch非常类似,它也可以实现线程间的计数等待,但是它的功能比CountDownLatch更加复杂且强大。CyclicBarrier可以理解为循环栅栏,这个计数器可以反复使用。比如,假设我们将计数器设置为10,那么凑齐第一批10个线程后,计数器就会归零,然后接着凑齐下一批10个线程,这就是它的内在含义。

    LOL和王者荣耀的玩家很多,许多人应该都有打大龙的经历,话说前期大家打算一起去偷大龙,由于前期大家都比较弱,需要五个人都齐了才能打大龙,这样程序该如何实现呢?本人很菜,开始我的代码是这么写的(哈哈大家不要纠结我的时间):

    public class KillDragon {
        /**
         * 模拟打野去打大龙
         */
       public static void dayePlayDragon(){
           System.out.println("打野在去打大龙的路上,需要10s");
       }
    
        /**
         * 模拟上单去打大龙
         */
        public static void shangdanPlayDragon(){
            System.out.println("上单在去打大龙的路上,需要10s");
        }
    
        /**
         * 模拟中单去打大龙
         */
        public static void zhongdanPlayDragon(){
            System.out.println("中单在去打大龙的路上,需要10s");
        }
    
        /**
         * 模拟ADC和辅助去打大龙
         */
        public static void adcAndFuzhuPlayDragon(){
            System.out.println("ADC和辅助在去打大龙的路上,需要10s");
        }
    
        /**
         * 模拟大家一起去打大龙
         */
        public static void killDragon()
        {
            System.out.println("打大龙...");
        }
    
        public static void main(String[] args)
        {
            dayePlayDragon();
            shangdanPlayDragon();
            zhongdanPlayDragon();
            adcAndFuzhuPlayDragon();
            killDragon();
        }
    

    结果如下:

    打野在去打大龙的路上,需要10s
    上单在去打大龙的路上,需要10s
    中单在去打大龙的路上,需要10s
    ADC和辅助在去打大龙的路上,需要10s
    打大龙...
    

     这完了,大家在路上的时间就花了40s了,显然是错误的。要是都这么干,对方把你塔都要偷光了。不行得改进下,怎么改呢,多线程并发执行,如是我改成了下面这样的,用volatile关键字。

    private static volatile int i = 4;
        public static void main(String[] args) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    long start = System.currentTimeMillis();
                    while (i!=0){
    
                    }
                    while (i==0) {
                        killDragon();
                        i--;
                        long t = System.currentTimeMillis() - start;
                        System.out.println("总共耗时:"+t+"毫秒");
                    }
                }
            }).start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    dayePlayDragon();
                    try {
                        Thread.sleep(10000);
                        i--;
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    shangdanPlayDragon();
                    try {
                        Thread.sleep(10000);
                        i--;
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    zhongdanPlayDragon();
                    try {
                        Thread.sleep(10000);
                        i--;
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    adcAndFuzhuPlayDragon();
                    try {
                        Thread.sleep(10000);
                        i--;
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    

    结果如下:

    打野在去打大龙的路上,需要10s
    上单在去打大龙的路上,需要10s
    中单在去打大龙的路上,需要10s
    ADC和辅助在去打大龙的路上,需要10s
    打大龙...
    总共耗时:10005毫秒
    

    结果似乎还不错,但是处理起来实在是有点麻烦,需要 while (i!=0)一直在那循环着。这时候学到了用 CyclicBarrier来处理,代码如下:

    public static void main(String[] args) {
            CyclicBarrier barrier = new CyclicBarrier(5);
            new Thread(new Runnable() {
                @Override
                public void run() {
                    long start = System.currentTimeMillis();
                    try {
                        barrier.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                    killDragon();
                    long t = System.currentTimeMillis() - start;
                    System.out.println("总共耗时:"+t+"毫秒");
                }
            }).start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    dayePlayDragon();
                    try {
                        Thread.sleep(10000);
                        barrier.await();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }).start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    shangdanPlayDragon();
                    try {
                        Thread.sleep(10000);
                        barrier.await();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }).start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    zhongdanPlayDragon();
                    try {
                        Thread.sleep(10000);
                        barrier.await();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }).start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    adcAndFuzhuPlayDragon();
                    try {
                        Thread.sleep(10000);
                        barrier.await();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    

    大家都没到达之前都等待,结果如下:

    打野在去打大龙的路上,需要10s
    上单在去打大龙的路上,需要10s
    中单在去打大龙的路上,需要10s
    ADC和辅助在去打大龙的路上,需要10s
    打大龙...
    总共耗时:10002毫秒
    

    CyclicBarrier相当于线程的计数器:

    CyclicBarrier初始化时规定一个数目,然后计算调用了CyclicBarrier.await()进入等待的线程数。当线程数达到了这个数目时,所有进入等待状态的线程被唤醒并继续。
    CyclicBarrier就象它名字的意思一样,可看成是个障碍, 所有的线程必须到齐后才能一起通过这个障碍。
    CyclicBarrier初始时还可带一个Runnable的参数, 此Runnable任务在CyclicBarrier的数目达到后,所有其它线程被唤醒前被执行。
    当然这样使用CyclicBarrier和使用CountDownLatch是没什么区别的,正如前文所说的CyclicBarrier的功能更加的复杂且强大。给大家看一个《实战Java高并发程序设计》一书上的一个例子。
    比如:司令下达命令,要求10个士兵去一起完成一项任务。这时,就会要求10个士兵先集合报道,接着,一起雄赳赳气昂昂地去执行任务。当10个士兵都执行完了任务,那么司机就可以对外宣称,任务完成。相比CountDownLatch,CyclicBarrier可以接受一个参数作为BarrierAction。所谓的BarrierAction就是当计数器一次计数完成后,系统会执行的动作。如下构造函数,其中,parties表示技术总数,也就是参与的线程总数。

    public CyclicBarrier(int parties, Runnable barrierAction)
    

    下面示例演示了上述任务场景

    public class CyclicBarrierDemo {
        public static class Soldier implements Runnable {
            private String soldier;
            private final CyclicBarrier cyclicBarrier;
    
            public Soldier(CyclicBarrier cyclicBarrier, String soldier) {
                this.soldier = soldier;
                this.cyclicBarrier = cyclicBarrier;
            }
    
            @Override
            public void run() {
                try {
                    cyclicBarrier.await();
                    doWork();
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
    
            }
    
            void doWork() {
                try {
                    Thread.sleep(Math.abs(new Random().nextInt() % 10000));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(soldier + ":任务完成");
            }
        }
    
        public static class BarrierRun implements Runnable {
    
            boolean flag;
            int N;
    
            public BarrierRun(boolean flag, int N) {
                this.flag = flag;
                this.N = N;
            }
    
            @Override
            public void run() {
                if (flag) {
                    System.out.println("司令:[士兵" + N + "个,任务完成!");
                } else {
                    System.out.println("司令:[士兵" + N + "个,集合完毕!");
                    flag = true;
                }
            }
        }
        
        public static void main(String args[]) {
            final int N = 10;
            Thread[] allSoldier = new Thread[N];
            boolean flag = false;
            CyclicBarrier cyclicBarrier = new CyclicBarrier(N, new BarrierRun(flag, N));
            System.out.println("集合队伍!");
            for (int i = 0; i < N; i++) {
                System.out.println("士兵" + i + "报道!");
                allSoldier[i] = new Thread(new Soldier(cyclicBarrier, "士兵" + i));
                allSoldier[i].start();
            }
        }
    }
    
    

    执行结果如下:

    集合队伍!
    士兵0报道!
    士兵1报道!
    士兵2报道!
    士兵3报道!
    士兵4报道!
    士兵5报道!
    士兵6报道!
    士兵7报道!
    士兵8报道!
    士兵9报道!
    司令:[士兵10个,集合完毕!
    士兵0:任务完成
    士兵2:任务完成
    士兵9:任务完成
    士兵3:任务完成
    士兵7:任务完成
    士兵8:任务完成
    士兵1:任务完成
    士兵4:任务完成
    士兵5:任务完成
    士兵6:任务完成
    司令:[士兵10个,任务完成!
    
  • 相关阅读:
    根据对象的属性排序数组
    将多维数组的元素全部取出,组成一维数组的方法
    微信公众号报错 config:invalid signature
    改造业务代码
    微信公众号调用扫一扫
    JQuery :contains选择器,可做搜索功能,搜索包含关键字的dom
    【转】超全功能测试方法集锦——(通用黑盒功能:测试新人必收攻略)
    Oracle 查看表空间的大小及使用情况sql语句
    oracle数据库 参数open_cursors和session_cached_cursor详解!
    使用MITab操作MapInfo地图文件
  • 原文地址:https://www.cnblogs.com/WangHaiMing/p/7830961.html
Copyright © 2020-2023  润新知