美文网首页
CountDownLatch、CyclicBarrie、Sema

CountDownLatch、CyclicBarrie、Sema

作者: 静听风雨落 | 来源:发表于2019-06-17 15:26 被阅读0次
    • CountDownLatch(计数器)

    1)简介
    CountDownLatch是一个计数器闭锁,通过它可以完成类似于阻塞当前线程的功能,即:一个线程或多个线程一直等待,直到其他线程执行的操作完成。CountDownLatch用一个给定的计数器来初始化,该计数器的操作是原子操作,即同时只能有一个线程去操作该计数器。调用该类await方法的线程会一直处于阻塞状态,直到其他线程调用countDown方法使当前计数器的值变为零,每次调用countDown计数器的值减1。当计数器值减至零时,所有因调用await()方法而处于等待状态的线程就会继续往下执行。这种现象只会出现一次,因为计数器不能被重置,如果业务上需要一个可以重置计数次数的版本,可以考虑使用CycliBarrier。
    2)使用场景
    在某些业务场景中,程序执行需要等待某个条件完成后才能继续执行后续的操作;典型的应用如并行计算,当某个处理的运算量很大时,可以将该运算任务拆分成多个子任务,等待所有的子任务都完成之后,父任务再拿到所有子任务的运算结果进行汇总。
    3)源码

    //count参数为计数器的起始值
    public CountDownLatch(int count) {  };
    //调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行
    public void await() throws InterruptedException { }; 
    //和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行
    public boolean await(long timeout, TimeUnit unit) throws InterruptedException { };  
    //将count值减1
    public void countDown() { };  
    

    4)实例
    CountDownLatch实现一个线程等待一组线程执行完毕后再开始执行:

     static class ThreadWithThread_0 extends Thread{
            private static int num = 5;
            private static CountDownLatch countDownLatch = new CountDownLatch(num);
    
            public void run() {
                System.out.println(Thread.currentThread().getName() + "开始运行  --- 继承Thread");
                try {
                    Thread.sleep(20000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " ".concat("运行完毕  --继承Thread"));
                countDownLatch.countDown();
            }
    
            public static void main(String[] args) {
    
                new Thread(){
                    public void run() {
                        System.out.println(Thread.currentThread().getName() + "等待其他线程运行完毕  --- 继承Thread");
                        try {
                            countDownLatch.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + " ".concat("其他线程运行完毕, 开始执行本线程  --继承Thread"));
    
                    }
                }.start();
    
                for (int i=0; i< num; i++) {
                    ThreadWithThread_0 threadWithThread = new ThreadWithThread_0();
                    threadWithThread.start();
                }
            }
        }
    

    执行结果

    Thread-0等待其他线程运行完毕  --- 继承Thread
    Thread-2开始运行  --- 继承Thread
    Thread-4开始运行  --- 继承Thread
    Thread-3开始运行  --- 继承Thread
    Thread-1开始运行  --- 继承Thread
    Thread-5开始运行  --- 继承Thread
    Thread-2 运行完毕  --继承Thread
    Thread-3 运行完毕  --继承Thread
    Thread-4 运行完毕  --继承Thread
    Thread-5 运行完毕  --继承Thread
    Thread-1 运行完毕  --继承Thread
    Thread-0 其他线程运行完毕, 开始执行本线程  --继承Thread
    

    CountDownLatch实现一组线程批量等待某个状态, 然后在同时开始运行, 不过只能使用一次:

    static class ThreadWithThread extends Thread{
            private static int num = 5;
            private static CountDownLatch countDownLatch = new CountDownLatch(num);
    
            public void run() {
                countDownLatch.countDown();
                System.out.println("线程等待发令枪发令  --- 继承Thread");
                try {
                    countDownLatch.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " ".concat("继承Thread"));
            }
    
            public static void main(String[] args) {
                for (int i=0; i< num; i++) {
                    ThreadWithThread threadWithThread = new ThreadWithThread();
                    threadWithThread.start();
                }
            }
    }
    

    执行结果

    线程等待发令枪发令  --- 继承Thread
    线程等待发令枪发令  --- 继承Thread
    线程等待发令枪发令  --- 继承Thread
    线程等待发令枪发令  --- 继承Thread
    线程等待发令枪发令  --- 继承Thread
    Thread-3 继承Thread
    Thread-0 继承Thread
    Thread-2 继承Thread
    Thread-4 继承Thread
    Thread-1 继承Thread
    
    • CyclicBarrie(回环栅栏)

    1)简介
    CyclicBarrier也是一个同步辅助类,它允许一组线程相互等待,直到到达某个公共屏障点(common barrier point)。通过它可以完成多个线程之间相互等待,只有当每个线程都准备就绪后,才能各自继续往下执行后面的操作。类似于CountDownLatch,它也是通过计数器来实现的。当某个线程调用await方法时,该线程进入等待状态,且计数器加1,当计数器的值达到设置的初始值时,所有因调用await进入等待状态的线程被唤醒,继续执行后续操作。因为CycliBarrier在释放等待线程后可以重用,所以称为循环barrier。CycliBarrier支持一个可选的Runnable,在计数器的值到达设定值后(但在释放所有线程之前),该Runnable运行一次,注,Runnable在每个屏障点只运行一个。
    2)源码

    //参数parties指让多少个线程等待至barrier状态, 参数barrierAction为当这些线程都达到barrier状态时会执行的内容。
    public CyclicBarrier(int parties, Runnable barrierAction) {
    }
    //参数parties指让多少个线程等待至barrier状态
    public CyclicBarrier(int parties) {
    }
    //挂起当前线程,直至所有线程都到达barrier状态再同时执行后续任务
    public int await() throws InterruptedException, BrokenBarrierException { };
    //这些线程等待至一定的时间,如果还有线程没有到达barrier状态就直接让到达barrier的线程执行后续任务。
    public int await(long timeout, TimeUnit unit)throws InterruptedException,BrokenBarrierException,TimeoutException { };
    

    3)实例
    CyclicBarrie实现一组线程等待某个状态, 然后同时开始执行, 可以使用多次:

    static class ThreadWithThread_2 extends Thread{
            private static int num = 5;
            private static CyclicBarrier cyclicBarrier = new CyclicBarrier(num);
    
            public void run() {
                try {
    
                    System.out.println(Thread.currentThread().getName() + "等待全组线程准备完毕");
                    cyclicBarrier.await();
                    System.out.println(Thread.currentThread().getName() +  "等待完毕, 开始运行");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
    
            }
    
            public static void main(String[] args) {
                for (int i=0; i< num; i++) {
                    ThreadWithThread_2 threadWithThread = new ThreadWithThread_2();
                    threadWithThread.start();
                }
            }
        }
    

    执行结果

    Thread-0等待全组线程准备完毕
    Thread-1等待全组线程准备完毕
    Thread-3等待全组线程准备完毕
    Thread-2等待全组线程准备完毕
    Thread-4等待全组线程准备完毕
    Thread-4等待完毕, 开始运行
    Thread-0等待完毕, 开始运行
    Thread-1等待完毕, 开始运行
    Thread-2等待完毕, 开始运行
    Thread-3等待完毕, 开始运行
    
    • Semaphore(信号量)

    1)简介
    Semaphore与CountDownLatch相似,不同的地方在于Semaphore的值被获取到后是可以释放的,并不像CountDownLatch那样一直减到底。它也被更多地用来限制流量,类似阀门的 功能。如果限定某些资源最多有N个线程可以访问,那么超过N个主不允许再有线程来访问,同时当现有线程结束后,就会释放,然后允许新的线程进来。有点类似于锁的lock与 unlock过程。相对来说他也有两个主要的方法:通过acquire()获取一个访问许可证, 通过release()释放一个访问许可证.
    2)源码

    //permits参数是表示允许几个线程访问
    public Semaphore(int permits) {
            sync = new NonfairSync(permits);
    }
    //permits参数是表示允许几个线程访问, fair表示是否是公平的
    public Semaphore(int permits, boolean fair) {
            sync = fair ? new FairSync(permits) : new NonfairSync(permits);
    }
    

    3)实例
    Semaphore实现一个资源同时只允许若干个线程同时访问:

     static class ThreadWithThread_1 extends Thread{
            private static int num = 8;
            private static Semaphore semaphore = new Semaphore(4);
    
            public void run() {
                try {
                    semaphore.acquire();
                    System.out.println("获得一个许可证, 开始访问");
                    Thread.sleep(20000);
                    semaphore.release();
                    System.out.println("释放一个许可证");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
            }
    
            public static void main(String[] args) {
                for (int i=0; i< num; i++) {
                    ThreadWithThread_1 threadWithThread = new ThreadWithThread_1();
                    threadWithThread.start();
                }
            }
        }
    

    执行结果

    Thread-1获得一个许可证, 开始访问
    Thread-0获得一个许可证, 开始访问
    Thread-2获得一个许可证, 开始访问
    Thread-3获得一个许可证, 开始访问
    Thread-1释放一个许可证
    Thread-4获得一个许可证, 开始访问
    Thread-0释放一个许可证
    Thread-5获得一个许可证, 开始访问
    Thread-3释放一个许可证
    Thread-6获得一个许可证, 开始访问
    Thread-2释放一个许可证
    Thread-7获得一个许可证, 开始访问
    Thread-4释放一个许可证
    Thread-5释放一个许可证
    Thread-6释放一个许可证
    Thread-7释放一个许可证
    
    

    总结

    • CountDownLatch主要是实现了1个或N个线程需要等待其他线程完成某项操作之后才能继续往下执行操作,描述的是1个线程或N个线程等待其他线程的关系。CyclicBarrier主要是实现了多个线程之间相互等待,直到所有的线程都满足了条件之后各自才能继续执行后续的操作,描述的多个线程内部相互等待的关系。
    • CountDownLatch是一次性的,而CyclicBarrier则可以被重置而重复使用。

    相关文章

      网友评论

          本文标题:CountDownLatch、CyclicBarrie、Sema

          本文链接:https://www.haomeiwen.com/subject/nqtafctx.html