zoukankan      html  css  js  c++  java
  • CountDownLatch、CyclicBarrier、Semaphore、Exchanger

    CountDownLatch:

    允许N个线程等待其他线程完成执行。无法进行重复使用,只能用一次。

    比如有2个任务A,它要等待其他4个任务执行完毕之后才能执行,此时就可以利用CountDownLatch来实现这种功能了。

    public class Test {
    
         public static void main(String[] args) {    
    
             final CountDownLatch latch = new CountDownLatch(2);
    
              
    
             new Thread(){
    
                 public void run() {
    
                     try {
    
                         System.out.println("子线程"+Thread.currentThread().getName()+"正在执行");
    
                        Thread.sleep(3000);
    
                        System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕");
    
                        latch.countDown();
    
                    } catch (InterruptedException e) {
    
                        e.printStackTrace();
    
                    }
    
                 };
    
             }.start();
    
              
    
             new Thread(){
    
                 public void run() {
    
                     try {
    
                         System.out.println("子线程"+Thread.currentThread().getName()+"正在执行");
    
                         Thread.sleep(3000);
    
                         System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕");
    
                         latch.countDown();
    
                    } catch (InterruptedException e) {
    
                        e.printStackTrace();
    
                    }
    
                 };
    
             }.start();
    
              
    
             try {
    
                 System.out.println("等待2个子线程执行完毕...");
    
                latch.await();
    
                System.out.println("2个子线程已经执行完毕");
    
                System.out.println("继续执行主线程");
    
            } catch (InterruptedException e) {
    
                e.printStackTrace();
    
            }
    
         } 
    
    }
    线程Thread-0正在执行
    线程Thread-1正在执行
    等待2个子线程执行完毕...
    线程Thread-0执行完毕
    线程Thread-1执行完毕
    2个子线程已经执行完毕
    继续执行主线程

    CyclicBarrier

    实现让N个线程等待至某个状态(达到初始化数量值)之后再全部同时执行,并且要全部线程都执行cyclicBarrier.await();后才执行线程后续逻辑。

    在初次的4个线程越过barrier状态后,又可以用来进行新一轮的使用,可重复使用。

    比如聚餐,一个人先到了就开始等待,又来一个。。直到全部人到期后,这时才能吃饭,全部吃完后由一个人去结账(runable),最后每个人去做自己的事情。

    public class Test {
    
        public static void main(String[] args) {
    
            int N = 4;
    
            CyclicBarrier barrier  = new CyclicBarrier(N);
    
            for(int i=0;i<N;i++)
    
                new Writer(barrier).start();
    
        } 
    
        static class Writer extends Thread{
    
            private CyclicBarrier cyclicBarrier;
    
            public Writer(CyclicBarrier cyclicBarrier) {
    
                this.cyclicBarrier = cyclicBarrier;
    
            }
    
     
    
            @Override
    
            public void run() {
    
                System.out.println("线程"+Thread.currentThread().getName()+"正在写入数据...");
    
                try {
    
                    Thread.sleep(5000);      //以睡眠来模拟写入数据操作
    
                    System.out.println("线程"+Thread.currentThread().getName()+"写入数据完毕,等待其他线程写入完毕");
    
                    cyclicBarrier.await();
    
                } catch (InterruptedException e) {
    
                    e.printStackTrace();
    
                }catch(BrokenBarrierException e){
    
                    e.printStackTrace();
    
                }
    
                System.out.println("所有线程写入完毕,继续处理其他任务...");
    
            }
    
        }
    
    }
    线程Thread-0正在写入数据...
    线程Thread-3正在写入数据...
    线程Thread-2正在写入数据...
    线程Thread-1正在写入数据...
    线程Thread-2写入数据完毕,等待其他线程写入完毕
    线程Thread-0写入数据完毕,等待其他线程写入完毕
    线程Thread-3写入数据完毕,等待其他线程写入完毕
    线程Thread-1写入数据完毕,等待其他线程写入完毕
    所有线程写入完毕,继续处理其他任务...
    所有线程写入完毕,继续处理其他任务...
    所有线程写入完毕,继续处理其他任务...
    所有线程写入完毕,继续处理其他任务...


      从上面输出结果可以看出,每个写入线程执行完写数据操作之后,就在等待其他线程写入操作完毕。

      当所有线程线程写入操作完毕之后,所有线程就继续进行后续的操作了。

    public class Test {
    
        public static void main(String[] args) {
    
            int N = 4;
    
            CyclicBarrier barrier  = new CyclicBarrier(N,new Runnable() {
    
                @Override
    
                public void run() {
    
                    System.out.println("当前线程"+Thread.currentThread().getName());    
    
                }
    
            });
    
             
    
            for(int i=0;i<N;i++)
    
                new Writer(barrier).start();
    
        } 
    
        static class Writer extends Thread{
    
            private CyclicBarrier cyclicBarrier;
    
            public Writer(CyclicBarrier cyclicBarrier) {
    
                this.cyclicBarrier = cyclicBarrier;
    
            }
    
     
    
            @Override
    
            public void run() {
    
                System.out.println("线程"+Thread.currentThread().getName()+"正在写入数据...");
    
                try {
    
                    Thread.sleep(5000);      //以睡眠来模拟写入数据操作
    
                    System.out.println("线程"+Thread.currentThread().getName()+"写入数据完毕,等待其他线程写入完毕");
    
                    cyclicBarrier.await();
    
                } catch (InterruptedException e) {
    
                    e.printStackTrace();
    
                }catch(BrokenBarrierException e){
    
                    e.printStackTrace();
    
                }
    
                System.out.println("所有线程写入完毕,继续处理其他任务...");
    
            }
    
        }
    
    }
    线程Thread-0正在写入数据...
    线程Thread-1正在写入数据...
    线程Thread-2正在写入数据...
    线程Thread-3正在写入数据...
    线程Thread-0写入数据完毕,等待其他线程写入完毕
    线程Thread-1写入数据完毕,等待其他线程写入完毕
    线程Thread-2写入数据完毕,等待其他线程写入完毕
    线程Thread-3写入数据完毕,等待其他线程写入完毕
    当前线程Thread-3
    所有线程写入完毕,继续处理其他任务...
    所有线程写入完毕,继续处理其他任务...
    所有线程写入完毕,继续处理其他任务...
    所有线程写入完毕,继续处理其他任务...

    从结果可以看出,当四个线程都到达barrier状态后,会从四个线程中选择一个线程去执行Runnable。

    Semaphore

    可以控同时访问的线程个数,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。

    假若一个工厂有5台机器,但是有8个工人,一台机器同时只能被一个工人使用,只有使用完了,其他工人才能继续使用。那么我们就可以通过Semaphore来实现:

    public class Test {
    
        public static void main(String[] args) {
    
            int N = 8;            //工人数
    
            Semaphore semaphore = new Semaphore(5); //机器数目
    
            for(int i=0;i<N;i++)
    
                new Worker(i,semaphore).start();
    
        } 
    
         
    
        static class Worker extends Thread{
    
            private int num;
    
            private Semaphore semaphore;
    
            public Worker(int num,Semaphore semaphore){
    
                this.num = num;
    
                this.semaphore = semaphore;
    
            }
    
             
    
            @Override
    
            public void run() {
    
                try {
    
                    semaphore.acquire();
    
                    System.out.println("工人"+this.num+"占用一个机器在生产...");
    
                    Thread.sleep(2000);
    
                    System.out.println("工人"+this.num+"释放出机器");
    
                    semaphore.release();            
    
                } catch (InterruptedException e) {
    
                    e.printStackTrace();
    
                }
    
            }
    
        }
    
    }
    工人0占用一个机器在生产...
    工人1占用一个机器在生产...
    工人2占用一个机器在生产...
    工人4占用一个机器在生产...
    工人5占用一个机器在生产...
    工人0释放出机器
    工人2释放出机器
    工人3占用一个机器在生产...
    工人7占用一个机器在生产...
    工人4释放出机器
    工人5释放出机器
    工人1释放出机器
    工人6占用一个机器在生产...
    工人3释放出机器
    工人7释放出机器
    工人6释放出机器

    下面对上面说的三个辅助类进行一个总结:

      1)CountDownLatch和CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不同:

        CountDownLatch一般用于某个线程A等待若干个其他线程执行完任务之后,它才执行;

        而CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行;

        另外,CountDownLatch是不能够重用的,而CyclicBarrier是可以重用的。

      2)Semaphore其实和锁有点类似,它一般用于控制对某组资源的访问权限。

    Exchanger

    Exchanger可以在两个线程之间交换数据,只能是2个线程,他不支持更多的线程之间互换数据。

    当线程A调用Exchange对象的exchange()方法后,他会陷入阻塞状态,直到线程B也调用了exchange()方法,然后以线程安全的方式交换数据,之后线程A和B继续运行。

    public class ThreadLocalTest {  
      
        public static void main(String[] args) {  
            Exchanger<List<Integer>> exchanger = new Exchanger<>();  
            new Consumer(exchanger).start();  
            new Producer(exchanger).start();  
        }  
      
    }  
      
    class Producer extends Thread {  
        List<Integer> list = new ArrayList<>();  
        Exchanger<List<Integer>> exchanger = null;  
        public Producer(Exchanger<List<Integer>> exchanger) {  
            super();  
            this.exchanger = exchanger;  
        }  
        @Override  
        public void run() {  
            Random rand = new Random();  
            for(int i=0; i<10; i++) {  
                list.clear();  
                list.add(rand.nextInt(10000));  
                list.add(rand.nextInt(10000));  
                list.add(rand.nextInt(10000));  
                list.add(rand.nextInt(10000));  
                list.add(rand.nextInt(10000));  
                try {  
                    list = exchanger.exchange(list);  
                } catch (InterruptedException e) {  
                    // TODO Auto-generated catch block  
                    e.printStackTrace();  
                }  
            }  
        }  
    }  
      
    class Consumer extends Thread {  
        List<Integer> list = new ArrayList<>();  
        Exchanger<List<Integer>> exchanger = null;  
        public Consumer(Exchanger<List<Integer>> exchanger) {  
            super();  
            this.exchanger = exchanger;  
        }  
        @Override  
        public void run() {  
            for(int i=0; i<10; i++) {  
                try {  
                    list = exchanger.exchange(list);  
                } catch (InterruptedException e) {  
                    // TODO Auto-generated catch block  
                    e.printStackTrace();  
                }  
                System.out.print(list.get(0)+", ");  
                System.out.print(list.get(1)+", ");  
                System.out.print(list.get(2)+", ");  
                System.out.print(list.get(3)+", ");  
                System.out.println(list.get(4)+", ");  
            }  
        }  
    }  

    8041, 3278, 7654, 2981, 7529,
    7091, 4699, 3542, 9739, 3587,
    1981, 3427, 7698, 8519, 4319,
    2119, 8179, 4338, 7676, 2617,
    2772, 6843, 9765, 8948, 418,
    7433, 7846, 9442, 6114, 6475,
    9142, 4858, 6511, 6401, 9709,
    6466, 9250, 3018, 8256, 8771,
    80, 8762, 7367, 8664, 9053,
    8008, 5456, 2018, 9806, 8194,

     
  • 相关阅读:
    【转】提高VR渲染速度的最好方法(经典转载)
    【转】提高VR渲染速度的关键
    【转】怎样提高VR渲染速度
    MySql学习05---数据类型
    MySql学习04----数据库表的基本操作
    MySql学习03----数据库基本操作
    MySql学习02----SQL编程的基本概念
    MySql学习01----数据库简介
    MySql学习路线00
    maven07-----maven中的聚合与继承
  • 原文地址:https://www.cnblogs.com/wade-luffy/p/5769380.html
Copyright © 2011-2022 走看看