• CyclicBarrier与CountDownLatch区别


    阻塞与唤醒方式的区别

    CountDownLatch计数方式

    CountDownLatch是减计数。调用await()后线程阻塞。调用countDown()方法后计数减一,当计数为零时,调用await()的线程被唤醒。

    CountDownLatch应用场景为:

    一个或一组线程等待另一组线程完成操作后恢复执行

    CountDownLatch例子: 模拟赛跑

    开始时一组运动员线程等待begin计数器(初始值为1),当主线程调用begin.countDown()后begin减1,计数器为0,这一组运动员线程同时起跑。主线程等待end计数器(初始值为10)。一个运动员线程到达终点后,调用end.countDown(),end计数器减1。当所有运动员都到达终点后,end计数器为0,主线程恢复执行。

    package CountDownLatch;
    
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class RaceSimulation {
        public static void main(String args[]){
            //比赛开始的倒数锁
            CountDownLatch begin=new CountDownLatch(1);
            //比赛结束的倒数锁
            CountDownLatch end=new CountDownLatch(10);
            //十个选手跑步线程
            final ExecutorService exec = Executors.newFixedThreadPool(10);
    
            for(int index= 0;index<10;++index){
                final int NO=index+1;
                Runnable run = new Runnable(){
                    @Override
                    public void run() {
                        try{
                            //如果计数不为0,则一直等待
                            //如果当前计数为0,此线程立即执行
                            begin.await();
                            Thread.sleep((long)(Math.random()*10000));
                            System.out.println("No."+NO+" arrived");
                        }catch(InterruptedException e){
                            e.printStackTrace();
                        }finally{
                            //每个选手到达终点时,end就减1
                            end.countDown();
                        }
                    }
                };
                exec.submit(run);
            }
            System.out.println("游戏开始:");
            //begin减1,开始游戏
            begin.countDown();
            //等待end变为0,即所有选手到达终点
            try {
                end.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("游戏结束");
            exec.shutdown();
        }
    }
    

    CyclicBarrier计数方式

    CyclicBarrier是加计数。调用await()后线程阻塞计数器加1,当所有线程都到达屏障被阻塞后,这一组线程才一起恢复执行。

    CyclicBarrier的应用场景

    一组线程到达一个屏障(即执行CyclicBarrier.await())时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活。CyclicBarrier默认的构造方法是CyclicBarrier(int parties),其参数表示屏障拦截的线程数量,每个线程调用await方法告诉CyclicBarrier我已经到达了屏障,然后当前线程被阻塞。

    CyclicBarier的例子

    package cyclicBarrier;
    
    import java.util.Random;
    import java.util.concurrent.BrokenBarrierException;
    import java.util.concurrent.CyclicBarrier;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    public class CyclicBarrierDemo {
        public static void main(String[] args) {
            int N=4;
            CyclicBarrier cyclicBarrier=new CyclicBarrier(N,new Runnable() {
                @Override
                public void run() {
                    System.out.println("线程"+Thread.currentThread().getName()+"正在执行所有线程到达屏障后执行的操作");
                }
            });
            ExecutorService exec=Executors.newFixedThreadPool(4);
            for(int i=0;i<N;++i) {
                Runnable r=()->{
                    try {
                        System.out.println("线程"+Thread.currentThread().getName()+"正在执行线程的操作");
                        //用睡眠代替线程的操作
                        Thread.sleep(new Random().nextInt(1000));
                        System.out.println("线程"+Thread.currentThread().getName()+"到达屏障");
                        cyclicBarrier.await();
                        System.out.println("线程"+Thread.currentThread().getName()+"越过屏障,线程执行完毕");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
               };
               exec.submit(r);
            }
            exec.shutdown();
        }
    }
    

    是否可以重用

    1. CountDownLatch不可以重用

    2. CyclicBarrier可以重用

    package cyclicBarrier;
    
    import java.util.Random;
    import java.util.concurrent.BrokenBarrierException;
    import java.util.concurrent.CyclicBarrier;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    public class CyclicBarrierDemo {
        public static void main(String[] args) {
            int N=4;
            CyclicBarrier cyclicBarrier=new CyclicBarrier(N,new Runnable() {
                @Override
                public void run() {
                    System.out.println("线程"+Thread.currentThread().getName()+"正在执行所有线程到达屏障后执行的操作");
                }
            });
            ExecutorService exec1=Executors.newFixedThreadPool(4);
            for(int i=0;i<N;++i) {
                Runnable r=()->{
                    try {
                        System.out.println("线程"+Thread.currentThread().getName()+"正在执行线程的操作");
                        //用睡眠代替线程的操作
                        Thread.sleep(new Random().nextInt(1000));
                        System.out.println("线程"+Thread.currentThread().getName()+"到达屏障");
                        cyclicBarrier.await();
                        System.out.println("线程"+Thread.currentThread().getName()+"越过屏障,线程执行完毕");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
               };
               exec1.submit(r);
            }
            exec1.shutdown();
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            System.out.println("CyclicBarrier重用");
            ExecutorService exec2=Executors.newFixedThreadPool(4);
    
            for(int i=0;i<N;++i) {
                Runnable r=()->{
                    try {
                        System.out.println("线程"+Thread.currentThread().getName()+"正在执行线程的操作");
                        //用睡眠代替线程的操作
                        Thread.sleep(new Random().nextInt(1000));
                        System.out.println("线程"+Thread.currentThread().getName()+"到达屏障");
                        cyclicBarrier.await();
                        System.out.println("线程"+Thread.currentThread().getName()+"越过屏障,线程执行完毕");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
               };
               exec2.submit(r);
            }
            exec2.shutdown();
        }
    }
    
  • 相关阅读:
    Android数据的四种存储方式SharedPreferences、SQLite、Content Provider和File
    android的五大布局(layout)
    json数据进行格式化
    将utf-8的中文或者字符都看成一个字符
    Mysql 中 trim 的用法
    生成密码函数
    Eclipse智能提示设置
    Java Jersey2使用总结
    Java对存储过程的调用方法
    Jersey框架
  • 原文地址:https://www.cnblogs.com/infoflow/p/7535845.html
Copyright © 2020-2023  润新知