JUC中的常用辅助类(CountDownLatchCyclicBarrierSemaphore)

Posted XeonYu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JUC中的常用辅助类(CountDownLatchCyclicBarrierSemaphore)相关的知识,希望对你有一定的参考价值。

上一篇:
JUC中的 StampedLock

CountDownLatch

减法计数器,用来更方便的实现多线程的同步。

举个例子,android开发中,进入主页一般都会调好几个接口,我们需要在这几个接口都调用完毕后执行下一步操作,那我们肯定不会一个一个调用,肯定是要多个接口同时去调用,都调用完毕后再执行下一步操作,这种场景就非常适合用CountDownLatch来实现了。

CountDownLatch的方法并不多,用起来也比较简单
在这里插入图片描述

我们来直接用一用:


    public static void main(String[] args) {

        int num = 5;


        System.out.println("主线程开始执行了");
        /*创建一个给初始值的计数器*/
        CountDownLatch countDownLatch = new CountDownLatch(num);


        for (int i = 1; i <= num; i++) {
            new Thread(() -> {

                try {
                    TimeUnit.MILLISECONDS.sleep(1000);

                    System.out.println(Thread.currentThread().getName() + "执行完毕了");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {

                    /*计数器减一*/
                    countDownLatch.countDown();
                }


            }, "Thread" + i).start();
        }

        try {

            /*等待计数器的值减为0  否则阻塞住后面代码*/
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


        System.out.println(num + "个接口都执行完毕了");


    }

运行结果:

在这里插入图片描述

可以看到,很方便的就实现了多线程的同步。


CyclicBarrier

翻译过来是 循环屏障 的意思

常用方法如下:
在这里插入图片描述

先来看构造方法:
在这里插入图片描述

传两个参数,

  • parties 表示在障碍被触发前需要调用await方法的线程数
  • barrierAction 表示障碍触发时要执行的任务

再来看看await方法:
在这里插入图片描述

实际调用了dowait,再来看看dowait方法:

private int dowait(boolean timed, long nanos)
        throws InterruptedException, BrokenBarrierException,
               TimeoutException {

        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            final Generation g = generation;

            if (g.broken)
                throw new BrokenBarrierException();

            if (Thread.interrupted()) {
                breakBarrier();
                throw new InterruptedException();
            }

            int index = --count;
            if (index == 0) {  // tripped
                boolean ranAction = false;
                try {
                    final Runnable command = barrierCommand;
                    if (command != null)
                        command.run();
                    ranAction = true;
                    nextGeneration();
                    return 0;
                } finally {
                    if (!ranAction)
                        breakBarrier();
                }
            }

            // loop until tripped, broken, interrupted, or timed out
            for (;;) {
                try {
                    if (!timed)
                        trip.await();
                    else if (nanos > 0L)
                        nanos = trip.awaitNanos(nanos);
                } catch (InterruptedException ie) {
                    if (g == generation && ! g.broken) {
                        breakBarrier();
                        throw ie;
                    } else {
                        // We're about to finish waiting even if we had not
                        // been interrupted, so this interrupt is deemed to
                        // "belong" to subsequent execution.
                        Thread.currentThread().interrupt();
                    }
                }

                if (g.broken)
                    throw new BrokenBarrierException();

                if (g != generation)
                    return index;

                if (timed && nanos <= 0L) {
                    breakBarrier();
                    throw new TimeoutException();
                }
            }
        } finally {
            lock.unlock();
        }
    }

代码太长了,这里就直接把源码复制过来了,可以看到,内部还是执行了减法,数值为0时,执行Runnable方法。

我们来简单用一用,以拼团为例,每三个人可以达成一个成团条件:

       public static void main(String[] args) {
        int num = 10;
        
        int parties = 3;//屏障点
        System.out.println("主线程开始执行了");
        /*构造器传入一个达成条件要执行await的数量以及达到条件后要执行的任务*/
        CyclicBarrier cyclicBarrier = new CyclicBarrier(parties, () -> System.out.println(Thread.currentThread().getName() + " 发现成团条件达成了...."));

        for (int i = 1; i <= num; i++) {

            int finalI = i;
            new Thread(() -> {

                try {
                    TimeUnit.MILLISECONDS.sleep(finalI * 1000L);
                    System.out.println(Thread.currentThread().getName() + "开始拼团");
                    /*调用一下await  就会触发*/
                    cyclicBarrier.await();
                } catch (Exception e) {
                    e.printStackTrace();
                }

            }, "线程" + i).start();
        }


        System.out.println("主线程执行到这了......");
    }

来看一看运行结果:
在这里插入图片描述

可以看到,CyclicBarrier 中的await方法不会阻塞住主线程。每当条件达成时,达到触发条件的当前线程就会执行一遍Runnable。但是如果没有达成条件,则会一直处于等待状态。


Semaphore

翻译叫做 信号量,通常我们叫做 计数信号量

主要用来限制多线程下的并发数量(限流)。

先来看看有哪些方法:

在这里插入图片描述

Semaphore维护了我们指定数量的许可证,通过acquire方法获取许可证,当许可证数量为0时,其他线程处于等待状态,通过Release方法释放(归还)许可证。归还后其他线程可以通过获取许可证去执行。

简单用一用:

    public static void main(String[] args) {

        /*指定许可证的数量为3*/
        Semaphore semaphore = new Semaphore(3);

        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                try {
                    /*获取许可证*/
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName() + "拿到了许可证");
                    TimeUnit.MILLISECONDS.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    System.out.println(Thread.currentThread().getName() + "====释放了许可证");
                    /*释放许可证*/
                    semaphore.release();
                }
            }, "线程" + i).start();
        }
    }

运行结果如下:

在这里插入图片描述

可以看到,同时只会有三个线程执行。


如果你觉得本文对你有帮助,麻烦动动手指顶一下,可以帮助到更多的开发者,如果文中有什么错误的地方,还望指正,转载请注明转自喻志强的博客 ,谢谢!

以上是关于JUC中的常用辅助类(CountDownLatchCyclicBarrierSemaphore)的主要内容,如果未能解决你的问题,请参考以下文章

JUC常用辅助类

JUC常用同步工具类——CountDownLatch,CyclicBarrier,Semaphore

JUC高级多线程_06:多线程下得常用辅助类

JUC常用类解析

重点知识学习(8.3)--[JUC常用类 || Java中的14把锁 || 对象头 || Synchronized 与 ReentrantLock]

8_JUC三大辅助类