zoukankan      html  css  js  c++  java
  • Java计数器之CountDownLatch、CyclicBarrier、Semaphore

    在Java里面有几种可以用于控制线程状态的方法,如CountDownLatch计数器、CyclicBarrier循环栅栏、Sempahore信号量。下面就分别演示下他们的使用方法:

    CountDownLatch

    CountDownLatch可以实现多线程之间的计数器,并实现阻塞功能。比如某个任务依赖于其他的两个任务,只有那两个任务执行结束后,它才能执行。

    import java.util.concurrent.CountDownLatch;
    
    public class CountDownLatchTest2 {
        public static void main(String[] args) {
            // 创建计数器,初始化为2
            final CountDownLatch latch = new CountDownLatch(2);
    
            new Thread(() -> {
                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(() -> {
                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正在执行
    等待2个子线程执行完毕...
    子线程Thread-1正在执行
    子线程Thread-0执行完毕
    子线程Thread-1执行完毕
    2个子线程已经执行完毕
    继续执行主线程
    


    如上图所示,线程1需要另两个线程结束后,才能继续执行。那么就可以在两个县城里面执行countDown(),然后主线程调用await()进行阻塞。

    CyclicBarrier 循环栅栏

    它有两层含义,一个是栅栏,一个是循环。先看栅栏,意思就是想一堵墙一样,可以同时对多个线程状态进行管理。

    如图所示,几个线程必须同时执行完,才能继续:

    
    import java.util.concurrent.BrokenBarrierException;
    import java.util.concurrent.CyclicBarrier;
    
    public class CyclicBarrierTest {
        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-1正在写入数据...
    线程Thread-2正在写入数据...
    线程Thread-3正在写入数据...
    线程Thread-0写入数据完毕,等待其他线程写入完毕
    线程Thread-1写入数据完毕,等待其他线程写入完毕
    线程Thread-3写入数据完毕,等待其他线程写入完毕
    线程Thread-2写入数据完毕,等待其他线程写入完毕
    所有线程写入完毕,继续处理其他任务...
    所有线程写入完毕,继续处理其他任务...
    所有线程写入完毕,继续处理其他任务...
    所有线程写入完毕,继续处理其他任务...
    

    循环的意思就是当计数减到0时,还可以继续使用,如:

    
    import java.util.concurrent.BrokenBarrierException;
    import java.util.concurrent.CyclicBarrier;
    
    public class CyclicBarrierTest3 {
        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();
            }
    
            try {
                Thread.sleep(25000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            System.out.println("CyclicBarrier重用");
    
            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.currentThread().getName()+"所有线程写入完毕,继续处理其他任务...");
            }
        }
    
    }
    

    输出:

    线程Thread-0正在写入数据...
    线程Thread-2正在写入数据...
    线程Thread-3正在写入数据...
    线程Thread-1正在写入数据...
    线程Thread-2写入数据完毕,等待其他线程写入完毕
    线程Thread-0写入数据完毕,等待其他线程写入完毕
    线程Thread-1写入数据完毕,等待其他线程写入完毕
    线程Thread-3写入数据完毕,等待其他线程写入完毕
    Thread-3所有线程写入完毕,继续处理其他任务...
    Thread-1所有线程写入完毕,继续处理其他任务...
    Thread-0所有线程写入完毕,继续处理其他任务...
    Thread-2所有线程写入完毕,继续处理其他任务...
    CyclicBarrier重用
    线程Thread-4正在写入数据...
    线程Thread-5正在写入数据...
    线程Thread-6正在写入数据...
    线程Thread-7正在写入数据...
    线程Thread-4写入数据完毕,等待其他线程写入完毕
    线程Thread-7写入数据完毕,等待其他线程写入完毕
    线程Thread-5写入数据完毕,等待其他线程写入完毕
    线程Thread-6写入数据完毕,等待其他线程写入完毕
    Thread-6所有线程写入完毕,继续处理其他任务...
    Thread-7所有线程写入完毕,继续处理其他任务...
    Thread-4所有线程写入完毕,继续处理其他任务...
    Thread-5所有线程写入完毕,继续处理其他任务...
    

    Semaphore信号量

    这个东西有点像连接池的感觉,某一时间只有几个线程能拿到资源,执行操作。

    比如下面车间工人在排队使用机器的例子:

    
    import java.util.concurrent.Semaphore;
    
    public class SemaphoreTest {
        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占用一个机器在生产...
    工人3占用一个机器在生产...
    工人4占用一个机器在生产...
    工人1释放出机器
    工人0释放出机器
    工人4释放出机器
    工人5占用一个机器在生产...
    工人2释放出机器
    工人7占用一个机器在生产...
    工人3释放出机器
    工人6占用一个机器在生产...
    工人5释放出机器
    工人6释放出机器
    工人7释放出机器
    

    总结

    1. CountDownLatch 可以实现计数等待,主要用于某个线程等待其他几个线程
    2. CyclicBarrier 实现循环栅栏,主要用于多个线程同时等待其他线程
    3. Semaphore 信号量,主要强调只有某些个数量的线程能拿到资源执行

    参考

  • 相关阅读:
    C#中关于@的用法
    c++ 中__declspec 的用法
    #pragma详细解释(一)
    memmove 和 memcpy的区别
    【niubi-job——一个分布式的任务调度框架】----安装教程
    [异能程序员]第一章 酒后事发(第一更)
    博客园的最后一篇博文,还是要离开了(附带个人博客源码分享)
    五一假期——技术之路上的坎儿
    deerlet-redis-client添加集群支持,邀请各路大神和菜鸟加入。
    从日常开发说起,浅谈HTTP协议是做什么的。
  • 原文地址:https://www.cnblogs.com/xing901022/p/8696550.html
Copyright © 2011-2022 走看看