当前位置: 首页 > news >正文

java源码 - CountDownLatch

开篇

  • CountDownLatch是一个同步工具类,用来协调多个线程之间的同步,或者说起到线程之间的通信(而不是用作互斥的作用)。

  • CountDownLatch能够使一个线程在等待另外一些线程完成各自工作之后,再继续执行。使用一个计数器进行实现。计数器初始值为线程的数量。当每一个线程完成自己任务后,计数器的值就会减一。当计数器的值为0时,表示所有的线程都已经完成了任务,然后在CountDownLatch上等待的线程就可以恢复执行任务。

  • CountDownLatch是一次性的,计数器的值只能在构造方法中初始化一次,之后没有任何机制再次对其设置值,当CountDownLatch使用完毕后,它不能再次被使用。


CountDownLatch的用法

  • CountDownLatch典型用法1:某一线程在开始运行前等待n个线程执行完毕。将CountDownLatch的计数器初始化为n new CountDownLatch(n) ,每当一个任务线程执行完毕,就将计数器减1 countdownlatch.countDown(),当计数器的值变为0时,在CountDownLatch上 await() 的线程就会被唤醒。一个典型应用场景就是启动一个服务时,主线程需要等待多个组件加载完毕,之后再继续执行。

  • CountDownLatch典型用法2:实现多个线程开始执行任务的最大并行性。注意是并行性,不是并发,强调的是多个线程在某一时刻同时开始执行。类似于赛跑,将多个线程放到起点,等待发令枪响,然后同时开跑。做法是初始化一个共享的CountDownLatch(1),将其计数器初始化为1,多个线程在开始执行任务前首先 coundownlatch.await(),当主线程调用 countDown() 时,计数器变为0,多个线程同时被唤醒。


CountDownLatch的demo

public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException{
        CountDownLatch countDownLatch = new CountDownLatch(2){
            @Override
            public void await() throws InterruptedException {
                super.await();
                System.out.println(Thread.currentThread().getName() +  " count down is ok");
            }
        };
        
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                //do something
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " is done");
                countDownLatch.countDown();
            }
        }, "thread1");
        
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " is done");
                countDownLatch.countDown();
            }
        }, "thread2");
        
        thread1.start();
        thread2.start();
        
        countDownLatch.await();
    }


CountDownLatch的类定义

  • CountDownLatch内部包含Sync类。
  • CountDownLatch内部包含Sync类的对象sync。
  • Sync类继承自AQS(神奇的AQS),构造函数设置AQS的state值为等待值。
public class CountDownLatch {

    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;
            }
        }
    }

    private final Sync sync;

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


CountDownLatch的等待过程

  • CountDownLatch通过await()进入等待。
  • CountDownLatch通过await(long timeout, TimeUnit unit)进入超时等待。
    public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

    public boolean await(long timeout, TimeUnit unit)
        throws InterruptedException {
        return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
    }



CountDownLatch的await()过程

  • await()通过sync.acquireSharedInterruptibly()获锁。
  • acquireSharedInterruptibly通过tryAcquireShared()尝试获锁。
  • tryAcquireShared()判断获锁成功与否的依据是AQS的state的值是否为零。
  • 获锁失败后通过doAcquireSharedInterruptibly()进入锁等待队列CLH。
    public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }


    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        // 尝试获锁失败
        if (tryAcquireShared(arg) < 0)
            // 
            doAcquireSharedInterruptibly(arg);
    }


    protected int tryAcquireShared(int acquires) {
        return (getState() == 0) ? 1 : -1;
    }


    private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head) {
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }



CountDownLatch的await(long timeout, TimeUnit unit)过程

  • await(long timeout, TimeUnit unit)通过sync.tryAcquireSharedNanos()获锁。
  • tryAcquireSharedNanos()通过doAcquireSharedNanos()尝试获锁。
  • tryAcquireShared()判断获锁成功与否的依据是AQS的state的值是否为零。
  • 获锁失败后通过doAcquireSharedNanos()进入锁等待队列CLH,和doAcquireSharedInterruptibly()方法相比增加了超时检测机制,通过LockSupport.parkNanos()实现超时。
    public boolean await(long timeout, TimeUnit unit)
        throws InterruptedException {
        return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
    }



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



    private boolean doAcquireSharedNanos(int arg, long nanosTimeout)
            throws InterruptedException {
        if (nanosTimeout <= 0L)
            return false;
        final long deadline = System.nanoTime() + nanosTimeout;
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head) {
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        failed = false;
                        return true;
                    }
                }
                nanosTimeout = deadline - System.nanoTime();
                if (nanosTimeout <= 0L)
                    return false;
                if (shouldParkAfterFailedAcquire(p, node) &&
                    nanosTimeout > spinForTimeoutThreshold)
                    LockSupport.parkNanos(this, nanosTimeout);
                if (Thread.interrupted())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }


CountDownLatch的唤醒过程

  • CountDownLatch通过sync.releaseShared(1)释放锁实现state的递减
  • tryReleaseShared()方法判断锁状态state==0,递减后值为0说明锁已经被释放。
  • releaseShared()释放锁成功后通过doReleaseShared()方法唤醒所有等待线程。
  • doReleaseShared()唤醒锁的过程是一个传播性的唤醒,通过线程A唤醒线程B,然后由线程B唤醒线程C的传播性依次唤醒所有等待线程。
    public void countDown() {
        sync.releaseShared(1);
    }

    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }

    protected boolean tryReleaseShared(int releases) {
        for (;;) {
            int c = getState();
            if (c == 0)
                return false;
            int nextc = c-1;
            if (compareAndSetState(c, nextc))
                return nextc == 0;
        }
    }

    private void doReleaseShared() {
        for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) {
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;            // loop to recheck cases
                    unparkSuccessor(h);
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            if (h == head)                   // loop if head changed
                break;
        }
    }


总结

CountDownLatch的工作原理,总结起来就两点(基于AQS实现):

  • 初始化锁状态的值为需要等待的线程数。
  • 判断锁状态是否已经释放,如果锁未释放所有等待锁的线程就会进入等待的CLH队列。
  • 如果锁状态已经释放,那么就会通过传播性唤醒所有的等待线程。

相关文章:

  • 推荐几十本DBA学习的书
  • 利用 Siblings一步实现多个同级div,只改变当前点击的div样式
  • 前端笔记-201808
  • 设置PHP最长运行时间
  • zabbix添加nginx中间件监控
  • 将MWeb的文章发布到自己做的网站(超级详细)
  • SELinux 宽容模式(permissive) 强制模式(enforcing) 关闭(disabled) 几种模式之间的转换...
  • 如何把本地文件上传到gitlab上已存在的工程里
  • Spring事务隔离级别详解
  • 反爬虫:利用ASP.NET MVC的Filter和缓存(入坑出坑)
  • 输入一条url后,发生了什么??
  • 超级鹰打码平台
  • Python中列表生成式和字典生成式练习
  • 复杂数据处理
  • 360病毒扫描蓝屏报错nvmini.sys
  • 《Java编程思想》读书笔记-对象导论
  • 2018天猫双11|这就是阿里云!不止有新技术,更有温暖的社会力量
  • Angularjs之国际化
  • css的样式优先级
  • iOS编译提示和导航提示
  • k8s如何管理Pod
  • Mocha测试初探
  • oschina
  • PHP 程序员也能做的 Java 开发 30分钟使用 netty 轻松打造一个高性能 websocket 服务...
  • 不发不行!Netty集成文字图片聊天室外加TCP/IP软硬件通信
  • 基于游标的分页接口实现
  • 容器服务kubernetes弹性伸缩高级用法
  • 使用阿里云发布分布式网站,开发时候应该注意什么?
  • 一道面试题引发的“血案”
  • 用 vue 组件自定义 v-model, 实现一个 Tab 组件。
  • 完善智慧办公建设,小熊U租获京东数千万元A+轮融资 ...
  • 曜石科技宣布获得千万级天使轮投资,全方面布局电竞产业链 ...
  • ###C语言程序设计-----C语言学习(3)#
  • #考研#计算机文化知识1(局域网及网络互联)
  • #使用清华镜像源 安装/更新 指定版本tensorflow
  • #预处理和函数的对比以及条件编译
  • (3)STL算法之搜索
  • (windows2012共享文件夹和防火墙设置
  • (zhuan) 一些RL的文献(及笔记)
  • (推荐)叮当——中文语音对话机器人
  • **Java有哪些悲观锁的实现_乐观锁、悲观锁、Redis分布式锁和Zookeeper分布式锁的实现以及流程原理...
  • .Net 中的反射(动态创建类型实例) - Part.4(转自http://www.tracefact.net/CLR-and-Framework/Reflection-Part4.aspx)...
  • .net利用SQLBulkCopy进行数据库之间的大批量数据传递
  • @Autowired自动装配
  • @CacheInvalidate(name = “xxx“, key = “#results.![a+b]“,multi = true)是什么意思
  • @Service注解让spring找到你的Service bean
  • @SuppressLint(NewApi)和@TargetApi()的区别
  • @Transient注解
  • [ JavaScript ] JSON方法
  • [ 网络基础篇 ] MAP 迈普交换机常用命令详解
  • []新浪博客如何插入代码(其他博客应该也可以)
  • [CentOs7]图形界面
  • [codeforces]Checkpoints
  • [CUDA 学习笔记] CUDA kernel 的 grid_size 和 block_size 选择
  • [Django 0-1] Core.Checks 模块