JUC工具包的加法、减法计数器

1. CountDownLatch(减法计数器)

CountDownLatch 被人称作减法计数器,实际应用的时候,按照JDK文档的解释,可以有两种场景适用。

用法一是作为发令枪,一个线程拿着发令枪,下令使其他多个线程同时开始进行

用法二是观察所有线程的开始或结束时刻,比如公交车需要等所有乘客上车才能关门,或者是所有乘客下车才能关门,也就是当所有线程开始或结束了,才开始执行主线程的后续方法

补充:new CountDownLatch(N)、coutDown()、await() 必须配合起来使用,创建计数器对象的时候赋的值是多少,coutDown() 就必须执行多少次,否则计数器是没有清零的,计数器就不会停止,其他线程也无法唤醒,所以必须保证计数器清零,所以coutDown() 的调用次数必须大于构造函数的参数值。

(1) 用法一(发令枪)

使用步骤

new CountDownLatch(1); 在一个线程中(一般是主线程),调用该方法,创建一个值为1的减法计数器

CountDownLatch.await(); 在所有其他线程的run方法开始时,调用其方法,使线程阻塞

CountDownLatch.countDown(); 到一定时机后在主线程中下令,调用该方法(减法计数器减一),使计数器为0,所有线程同时进入执行状态

代码示例

public static void main(String[] args) {
        //1.创建一个线程发令枪(值为1的计数器)
        CountDownLatch startSingle = new CountDownLatch(1);

        for (int i = 0; i < 5; i++) {
            new Thread(()->{
                System.out.println("我是"+Thread.currentThread().getName()+",正在等待下令");
                try {
                    startSingle.await();    //2.调用计数器await()方法使其等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("我是"+Thread.currentThread().getName()+",开始执行");
            },"线程"+(i+1)).start();
        }
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("=========");
        System.out.println("我是Main线程,马上开枪下令");
        System.out.println("=========");
        System.out.println("boom!开跑");
        startSingle.countDown(); //3. 调用countDown()方法,使计数器-1,进行下令
    }

JUC工具包的加法、减法计数器

(2) 用法二(记录所有线程的开始或结束时刻)

使用步骤

new CountDownLatch(N); 在一个线程中(一般是主线程),调用该方法,创建一个值为N的减法计数器

countDownLatch.countDown(); 在各个线程的执行方法中,调用该方法(减法计数器减一),使计数器减一,记录当前线程刚开始或已结束

countDownLatch.await(); 在主线程中调用方法,使其阻塞,等待接收计数器值为0

代码示例

static void allStartOrEnd(){
        CountDownLatch allStartSingle = new CountDownLatch(5); //1. 在主线程中创建一个值为N的减法计数器
        CountDownLatch allEndSingle = new CountDownLatch(5);

        for (int i = 0; i < 5; i++) {
            new Thread(()->{
                allStartSingle.countDown(); //2. 在线程的执行任务中,使减法计数器-1
                System.out.println("我是"+Thread.currentThread().getName()+",开始执行");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("我是"+Thread.currentThread().getName()+",执行完毕,已结束");
                allEndSingle.countDown(); //减法计数器-1
            },"线程"+(i+1)).start();
        }

        try {
            allStartSingle.await(); //3. 使主线程阻塞,等待计数器为空
            try {
                Thread.sleep(50); // 主线程休眠一小会。避免打印消息比线程执行任务的打印消息快
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("我是主线程,观察到线程开始的计数器值为0,已知晓所有线程都已开始执行");
            System.out.println("==============");
            allEndSingle.await();
            System.out.println("我是主线程,观察到线程结束的计数器值为0,已知晓所有线程都已结束");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

JUC工具包的加法、减法计数器

2. CyclicBarrier(加法计数器)

加法计数器构造器可以传入两个参数
JUC工具包的加法、减法计数器
一般使用的时候,是将要定时(或者说是定值)完成的任务,放入加法计数器中,并赋予其放行任务的值,在开启其他线程使,调用cyclicBarrier.await();方法,使值+1,当值到达放行值的时候,就执行该任务

比如一辆车,除了司机最多坐4人,司机(也就是这里的加法计数器)要求坐满了才开车,每当一个人上车,人数就+1,到达4个,就开车

使用步骤

new CyclicBarrier(N,new Runnable()); 主线程中创建一个加法计数器

cyclicBarrier.await(); 一般是在其他线程执行时使计数器+1

代码示例

 public static void main(String[] args) {
        // 1. new CyclicBarrier(int N,new Runable()); 在主线程中创建一个加法计数器,并赋予其放行值和要执行的任务
        CyclicBarrier cyclicBarrier = new CyclicBarrier(5,new Runnable() { //为了明显,没有用lamdba表达式
            @Override
            public void run() {
                System.out.println("==========");
                System.out.println("加法计数器值到了5,放行该任务,且加法计数器的值重置为0");
            }
        });
        for (int i = 0; i < 5; i++) {
            new Thread(()->{
                try {
                    System.out.println(Thread.currentThread().getName()+"正在执行,任务是使计数器值+1");
                    cyclicBarrier.await();  // 2. cyclicBarrier.await(); 使计数器+1 这里的await()方法是让加法计数器+1
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            },"线程"+(i+1)).start();
        }
    }
上一篇:JAVA程序猿怎么才干高速查找到学习资料?


下一篇:JUC之ReentrantLock