JUC包中的CountDownLatch源码实现分析

CountDownLatch是JUC包中提供的线程同步工具,使用CountDownLatch可以实现一个或多个线程等待直到一组操作在其他线程中被执行完成。

CountDownLatch基于AQS实现,代码不多,所以很好分析。本文只分析CountDownLatch实现, 关于AQS的实现在另外一篇文章中叙述。

下面是CountDownLatch的类图:

接下来贴上来CountDownLatch的源码:

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);
    }
    public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }
    public boolean await(long timeout, TimeUnit unit)
        throws InterruptedException {
        return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
    }
    public void countDown() {
        sync.releaseShared(1);
    }
    public long getCount() {
        return sync.getCount();
    }
    public String toString() {
        return super.toString() + "[Count = " + sync.getCount() + "]";
    }
}

首先,实例化CountDownLatch,没有太多需要分析的,只是在构造函数中传入一个数字,我们可以理解为“总操作数”。因为是多线程共享访问这个操作数,所以后续会调用AQS中的相关共享访问方法。

其次,await()为等待函数, 当剩余的“操作数”大于0时候,阻塞当前线程。通过Sync调用AQS:: acquireSharedInterruptibly(),代码如下:

public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }

可见acquireSharedInterruptibly()会回调tryAcquiredShred()方法,去获取共享模式以进行后续的操作,如果获取成功则会继续进行后续的操作,获取失败则阻塞。

tryAcquiredShared实现时候要注意返回值有三个:负值表示失败,零表示成功获取共享模式但是后续的获取共享模式不会成功,正值表示获取共享模式成功并且后续再次获取也可能成功。所以如果操作数大于0时候要返回负值,这样才能阻塞。CountDownLatch::tryAcquireShared代码就是这么实现的,如下:

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

第三,countDown()表示消耗一个操作数, 通过countDown()调用 AQS::releaseShared()方法,该方法判断是否消耗成功,如果成功则返回true,否则返回false。

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

调用tryReleaseShared() 返回true表示允许一个await()的线程获得共享资源,中断阻塞。

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

原文地址:https://blog.51cto.com/4123339/2462056

时间: 2024-10-31 01:23:38

JUC包中的CountDownLatch源码实现分析的相关文章

Eclipse中如何快速查看jar包中 的class源码

我们查看jar源码时,一般是安装个jd-gui,把jar拷出来,然后从jd-gui中打开jar再查看源码,这个过程不免有些麻烦,当然,本篇所讲的快速查看的方法也没什么高科技手段,只是将jd-gui集成在Eclipse中,然后就可以在Eclipse中直接打开class了,这样会不但操作方便也会节省不少时间,具体步骤: 下载插件:jd-eclipse-site-1.0.0-RC2.zip 打开Eclipse-->Help-->Install New Software: 在Name框中命名,然后点击

android.support.v4包中的LruCache源码简读

package android.util; import java.util.LinkedHashMap; import java.util.Map; /** * A cache that holds strong references to a limited number of values. Each time * a value is accessed, it is moved to the head of a queue. When a value is * added to a fu

Javac源码简单分析之解析和填充符号表

一.说明 符号表是由一组符号地址和符号信息构成的表格.符号表中所登记的信息在编译的不同阶段都要用到,在语义分析(后面的步骤)中,符号表所登记的内容将用于语义检查和产生中间代码,在目标代码生成阶段,党对符号名进行地址分配时,符号表是地址分配的依据. 二.主要的类与方法 解析和填充符号表这个过程主要由com.sun.tools.javac.comp.Entry及com.sun.tools.javac.comp.MemberEnter两个类来实现的. com.sun.tools.javac.comp.

JUC同步器框架AbstractQueuedSynchronizer源码图文分析

JUC同步器框架AbstractQueuedSynchronizer源码图文分析 前提 Doug Lea大神在编写JUC(java.util.concurrent)包的时候引入了java.util.concurrent.locks.AbstractQueuedSynchronizer,Abstract Queued Synchronizer,也就是"基于队列实现的抽象同步器",一般我们称之为AQS.其实Doug Lea大神编写AQS是有严谨的理论基础的,他的个人博客上有一篇论文<

十一、JUC包中的锁

JUC,即java.util.concurrent. 悲观锁和乐观锁 悲观锁和乐观锁是一种思想. 悲观锁,持有一种悲观的态度,认为会出现很坏的情况,所以,先做预防措施.独占锁是一种悲观锁,synchronized就是一种独占锁. 而乐观锁,则是持有一种持有种乐观的态度,认为不会出现什么问题,有问题了再说. 对于常用多线程编程的人估计知道,在jdk5之前,在多线程编程的时候,为了保证多个线程对一个对象同时进行访问时,我们需要加同步锁synchronized,保证对象的在使用时的正确性,但是加锁的机

下载外部jar包后,链接源码和javadoc.jar

今天下载了一个Apache Common的一个jar包,对于引入源码和JavaDoc有了新的认识,在这里记录一下. Binaries是指二进制文件,包含使用的jar包.Source是指源码. xxx.tag.gz是在Linux系统下使用的压缩包,xxx.zip是Windows系统使用的. 下载了两个zip包后, commons-collections4-4.1-bin.zip解压下的文件夹 其中,commons-collections4-4.1.jar是可以使用的jar包.commons-col

Python学习第六天----Linux内存管理、进程管理、RPM包安装管理及源码安装软件

Linux内存管理.进程管理.RPM包安装管理及源码安装软件 一.交换分区     交换分区其实就相当于Windows系统下的虚拟内存的概念,当物理内存不够用的时候,由操作系统将硬盘的一块区域划分出来作为内存使用.具体使用方法如下:      [[email protected] ~]# fdisk -l 磁盘 /dev/sdb:16.1 GB, 16106127360 字节,31457280 个扇区 Units = 扇区 of 1 * 512 = 512 bytes 扇区大小(逻辑/物理):5

探秘Tomcat(一)——Myeclipse中导入Tomcat源码

前言:有的时候自己不知道自己是井底之蛙,这并没有什么可怕的,因为你只要蜷缩在方寸之间的井里,无数次的生活轨迹无非最终归结还是一个圆形:但是可怕的是有一天你不得不从井里跳出来生活,需要重新审视井以外的生活,你就会发现世界如此美好,我知道的如此的少! 好比,但你看到如下代码 namespace Singleton { public class Singleton { private static Singleton singleton; private Singleton() { } public

在eclipse中关联android源码

1打包源码成jar: 1 新建一个java项目 2  import  想打包的源码文件 3 export 这个文件 : 选择java->jar file .  这里会让你选择输出路径 2 添加源码关联: 1 在 项目右键-> properties 中的 java build path -> libraries 中选择 想要关联的 jar包, 选择里面的 source attachment . 2 edit -> 选择刚才打包的jar 文件 3 完成 在eclipse中关联andro