zoukankan      html  css  js  c++  java
  • Java多线程系列——计数器 CountDownLatch

    简介:

    CountDownLatch 是一个非常实用的多线程控制工具类,通常用来控制线程的等待,它可以让某个线程等待直到倒计时结束

    CountDownLatch 提供了两个主要的方法,await()、countDown()。

    • await:使当前线程阻塞,等待计数器为 0

    • countDown:计数器减一,计数为零时,释放所有在等待的线程

    实例:

    public class CountDownLatchDemo implements Runnable {
        static final CountDownLatch end = new CountDownLatch(10);
        static final CountDownLatchDemo demo = new CountDownLatchDemo();
        @Override
        public void run() {
            try {
                Thread.sleep(new Random().nextInt(10) * 1000);
                System.out.println("check complete...");
                end.countDown();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        public static void main(String[] args) throws InterruptedException {
            ExecutorService exec = Executors.newFixedThreadPool(10);
            for (int i = 0;i < 10;i++) {
                exec.submit(demo);
            }
            end.await();
            System.out.println("Fire!");
            exec.shutdown();
        }
    }

    原理解析:

    countDownLatch 的计数是通过一个共享变量(volatile)实现的,下面分析它的三个核心函数:构造函数,CountDownLatch(int count);阻塞线程,await();计数器减一,countDown()。

    CountDownLatch(int count)

    public CountDownLatch(int count) {
      if (count < 0) throw new IllegalArgumentException("count < 0");
      this.sync = new Sync(count);
    }

    其中 Sync 是 CountDownLatch 的内部类,并且 Sync 继承了 AbstractQueuedSynchronizer

    private static final class Sync extends AbstractQueuedSynchronizer {
      private static final long serialVersionUID = 4982264981922014374L;
    
      Sync(int count) {
        setState(count);
      }
    
      int getCount() {
        return getState();
      }
    
      protected int tryAcquireShared(int acquires) {
        return (getState() == 0) ? 1 : -1;
      }
    
      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;
        }
      }
    }
    View Code

    其中 setState 是设置 AbstractQueuedSynchronizer 中 state 变量,该变量声明了 volatile。

    State 就是 countDownLatch 中的计数器。

    await()

    public void await() throws InterruptedException {
      sync.acquireSharedInterruptibly(1);
    }
    public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
          throws InterruptedException {
      if (Thread.interrupted())
        throw new InterruptedException();
      return tryAcquireShared(arg) >= 0 ||
        doAcquireSharedNanos(arg, nanosTimeout);
    }

    acquireSharedInterruptibly() 的作用是获取共享锁,如果当前线程处于中断状态,则抛出 InterruptedException,否则,调用 tryAcquireShared(arg) 尝试获取共享锁,如果锁计数器 = 0,则表示锁为可获取状态,返回 1,否则,锁为不可获取状态,则返回 -1。

    doAcquireSharedNanos() 会使当前线程一直等待,直到当前线程获取到共享锁(或线程被中断)才返回。

    countDown()

    public void countDown() {
      sync.releaseShared(1);
    }
    public final boolean releaseShared(int arg) {
      if (tryReleaseShared(arg)) {
        doReleaseShared();
        return true;
      }
      return false;
    }

    releaseShared() 目的是让当前线程释放它所持有的共享锁。

    tryReleaseShared() 的作用是释放共享锁,将锁计数器的值减一。

    总结

    CountDownLatch 是通过共享锁实现的。CountDownLatch 构造函数传递 int 参数,该参数是计数器的初始状态,表示共享锁最多能被 count 个线程同时获取。

    当某线程调用 CountDownLatch 的 await 方法时,该线程会等待共享锁可用时(计数器为 0 时),才能获取共享锁,进而继续执行。

    每次执行 countDown 时,会将计数器减一。

    参考资料

    Java多线程系列--“JUC锁”09之 CountDownLatch原理和示例

     
  • 相关阅读:
    python bottle学习(二)加载配置文件
    python bottle学习(一)快速入门
    python 文件读写模式r,r+,w,w+,a,a+的区别(附代码示例)
    Centos 虚拟机网络问题,网卡起不来,重启network服务失败
    机器学习框架MXnet安装步骤
    linux机器之间配置ssh无密访问
    好多年没写点技术相关的东西了,今天回来看看,找找当年做程序员的感觉
    vue路由传参的三种基本方式
    JavaScript判断对象是否包含某个属性的几种方法
    CSS实现三栏布局(左边固定、右边固定、中间自适应)的五种方式
  • 原文地址:https://www.cnblogs.com/zhengbin/p/9223635.html
Copyright © 2011-2022 走看看