关于java:Java高并发编程基础三大利器之CountDownLatch

40次阅读

共计 3908 个字符,预计需要花费 10 分钟才能阅读完成。

# 引言
上一篇文章咱们介绍了 AQS 的信号量 Semaphore《Java 高并发编程根底三大利器之 Semaphore》,接下来应该轮到CountDownLatch 了。

什么是 CountDownLatch

CountDownLatch是通过一个计数器来实现的,计数器的初始值是线程的数量。每当一个线程执行结束后,计数器的值就减 1,当计数器的值为 0 时,示意所有线程都执行结束,而后在闭锁上(调用 await 办法的线程)期待的线程就能够复原工作了。

利用场景

CountDownLatch能够用来干什么呢?有什么利用场景?理论我的项目中有利用的场景吗?这应该才是大家比较关心的。咱们先来看看官网提供的例子是如何进行利用的 https://docs.oracle.com/javas…
官网提供了两个 demo 我间接把它转成了图片顺带举荐下这个代码转图片的网址 https://www.dute.org/code-sna… 还挺好用的。

官网 demo1

The first is a start signal that prevents any worker from proceeding until the driver is ready for them to proceed;
The second is a completion signal that allows the driver to wait until all workers have completed.

  • 第一个开始信号 (startSignal) 会阻止任何工人 (worker ) 开始工作,在司机到来之前。说白了就是司机没来工人就不能干活。
  • 第二个是实现信号(doneSignal),容许司机 Driver 期待,直到所有的工人实现. 说白了就是司机要等到所有工人竣工为止。

官网 demo2

Another typical usage would be to divide a problem into N parts, describe each part with a Runnable that executes that portion and counts down on the latch, and queue all the Runnables to an Executor. When all sub-parts are complete, the coordinating thread will be able to pass through await.

另一种典型的用法就是把一个大工作拆分 N 个局部,让多个线程(Worker)执行,每个线程(Worker)执行完本人的局部计数器就减 1,当所有子局部都实现后,Driver 才持续向下执行才继续执行。
就好比富士康手机加工的流水线一样,组装一步手机须要一条条的流水线来相互配合实现。一条条流水线(Worker),每条线都干本人的活。有的流水线是贴膜的,有的流水线是打螺丝的,有的流水线是质检的、有的流水线充电的、有的流水线贴膜的。等这些流水线都干完了就把一部手机组装实现了。


下面两个就是官网提供的 demo,上面我再来两个咱们平时开发中能够用到的栗子:

多个线程期待:模仿并发,让并发线程一起执行。

有时候咱们写了接口想去压测下它,看看它的最大并发数大略是多少。当然咱们能够应用 Jmeter 来进行压测,然而有时候咱们不想去下载工具,其实就能够借助 CountDownLatch 来实现。

/**
 * @author: 公众号:java 金融
 */
public class TestCountDownLatch1 {public static void main(String[] args) throws InterruptedException {CountDownLatch countDownLatch = new CountDownLatch(1);
          for (int i = 0; i < 5; i++) {new Thread(() -> {
                    try {
                         // 所有申请都阻塞在这,期待
                         countDownLatch.await();
                         // 调用测试接口
                         System.out.println(Thread.currentThread().getName() + "开始执行……");
                    } catch (InterruptedException e) {e.printStackTrace();
                    }
               }).start();}
          // 让申请都筹备好
          Thread.sleep(2000);
          // 让所有申请对立申请
          countDownLatch.countDown();}
}

咱们通过CountDownLatch.await(),让多个参与者线程启动后阻塞期待,而后在主线程 调用CountDownLatch.countdown() 将计数减为0,让所有线程一起往下执行;以此实现了多个线程在同一时刻并发执行,来模仿并发申请的目标。

单个线程期待:多个线程 (工作) 实现后,进行汇总合并

/**
 * @author: 公众号:java 金融
 */
public class TestCountDownLatch1 {public static void main(String[] args) throws InterruptedException {
          int count = 3;
          CountDownLatch countDownLatch = new CountDownLatch(count);
          for (int i = 0; i < count; i++) {
               final int index = i;
               new Thread(() -> {
                    try {Thread.sleep(1000 + ThreadLocalRandom.current().nextInt(1000));
                         System.out.println("finish" + index + Thread.currentThread().getName());
                    } catch (InterruptedException e) {e.printStackTrace();
                    }finally{countDownLatch.countDown();
                    }
               }).start();}
          countDownLatch.await();// 主线程在阻塞,当计数器 ==0,就唤醒主线程往下执行。System.out.println("主线程: 在所有工作运行实现后,进行后果汇总");
     }
}

这种场景应该是用的最多了, 比方咱们关上一个电商的集体核心页面,咱们须要调用,用户信息接口、用户订单接口、用户会员信息等接口,而后合并后一起给到前端,假如每个接口最长耗时为 1s,如果咱们同步调用的话最大耗时工夫是 3s,如果咱们采纳异步调用而后合并后果,所以最大的耗时工夫是3s。每个接口调用返回数据后调用countDown 办法,让计数器进行减 1,当把计数器减为 0 时的这个线程会去唤醒主线程,让它持续往下走。

CountDownLatch 实现原理

CountDownLatch是通过 AQSstate字段来实现的一个计数器,计数器的初始值(state的值)为 new CountDownLatch 设置的数量,每次调用 countDown 的时候,state 的值会进行减 1,最初某个线程将 state 值减为 0 时,会把调用了 await() 进行阻塞期待的线程进行唤醒。CountDownLatch重写了 tryReleaseShared 这个办法,只有当 state 这个字段被设置为 0 时,也就是 tryReleaseShared 返回 true 的状况就会执行 doReleaseShared 办法,把调用了 await 的线程进行唤醒。

  public final boolean releaseShared(int arg) {if (tryReleaseShared(arg)) {doReleaseShared();
            return true;
        }
        return false;
    }
 protected boolean tryReleaseShared(int releases) {
            // Decrement count; signal when transition to zero
            for (;;) {int c = getState();
                if (c == 0)
                    return false;
                int nextc = c-1;
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }

CountDownLatch的其余源码就不进行剖析了,
置信看了这两篇文章《Java 高并发编程根底之 AQS》、《Java 高并发编程根底三大利器之 Semaphore》再来看这个还是比拟轻松的。

总结

  • CountDownLatch不能从新初始化或者批改 CountDownLatch 外部计数器的值。
  • CountDownLatchSemaphore 在应用 AQS 的形式上很类似,在同步状态中都是保留的是以后的计数值。
  • CountDownLatch的作用就是容许一个或多个线程期待其余线程实现操作,看起来有点相似join() 办法,但其提供了比 join() 更加灵便的 API。
  • CountDownLatch能够手动管制在 n 个线程里调用 ncountDown()办法使计数器进行减一操作,也能够在一个线程里调用 n 次执行减一操作。
  • join() 的实现原理是不停查看 join 线程是否存活,如果 join 线程存活则让以后线程永远期待。所以两者之间相对来说还是 CountDownLatch 应用起来较为灵便。

完结

  • 因为本人满腹经纶,难免会有纰漏,如果你发现了谬误的中央,还望留言给我指出来, 我会对其加以修改。
  • 如果你感觉文章还不错,你的转发、分享、赞叹、点赞、留言就是对我最大的激励。
  • 感谢您的浏览, 非常欢送并感谢您的关注。

站在伟人的肩膀上摘苹果:
https://javajr.cn/
https://zhuanlan.zhihu.com/p/…

正文完
 0