【Java并发工具】CountDownLatch

目录

1 前言

2 功能介绍

3 基于AbstractQueuedSynchronizer的实现

3.1 同步器Sync

3.2 构造方法

3.3 await方法

3.4 countDown方法

4 一次性使用


1 前言

本人使用jdk8版本。

CountDownLatch 允许一个或多个线程等待其他线程完成操作,作用与Thread.join()相似,join可以参考:Thread.join()原理

2 功能介绍

在JDK 1.5之后的并发包中提供的 CountDownLatch 也可以实现 join 的功能,并且比join的功能更多,示例如下:

public class CountDownLatchTest {

    static CountDownLatch c=new CountDownLatch(2);

    public static void main(String[] args) throws InterruptedException{
        new Thread(new Runnable(){
            @Override
            public void run(){
            System.out.println(1);
            c.countDown();
            System.out.println(2);
            c.countDown();
        }
     }).start();

     c.await();
     System.out.println("3");
    }
}

上面代码输出结果为1,2,3。main线程会一直阻塞在c.await()上,直到线程中两次c.countDown()使构造方法中传入的2变成0。

CountDownLatch 的构造函数接收一个int 类型的参数作为计数器,如果你想等待N个点完成,这里就传入N

当我们调用 CountDownLatchcountDown 方法时,N 就会 -1CountDownLatchawait 方法会阻塞当前线程,直到 N 变成零
由于 countDown 方法可以用在任何地方,所以这里说的 N 个点,可以是 N个线程,也可以是 1个线程里的N个执行步骤。用在多个线程时,只需要把这个 CountDownLatch 的引用传递到线程里即可。

如果有某个解析 sheet 的线程处理得比较慢,我们不可能让主线程一直等待,所以可以使用另外一个带指定时间的 await 方法—— await(long time,TimeUnit unit),这个方法等待特定时间后,就会不再阻塞当前线程。

3 基于AbstractQueuedSynchronizer的实现

CountDownLatch有一个静态内部类sync,它是AbstractQueuedSynchronizer子类,并针对自己的功能重写了AbstractQueuedSynchronizer的构造方法、getCount()、tryAcquireShared()、tryReleaseShared()。CountDownLatch.await()调用的是同步器的acquireSharedInterruptibly(),CountDownLatch.countDown()调用的是同步器的releaseShared()。

可以参考:AbstractQueuedSynchronizer共享式同步状态获取与释放

3.1 同步器Sync

    private final Sync sync;

    private static final class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 4982264981922014374L;

        Sync(int count) {
            setState(count);        // 设置锁被获取的个数
        }

        int getCount() {
            return getState();      // 获取锁被获取的个数
        }

        // 若锁的获取数位0时获取成功,否则获取失败且当前线程阻塞
        protected int tryAcquireShared(int acquires) {
            return (getState() == 0) ? 1 : -1;
        }

        // 将锁的获取数-1,当减至0时返回true且阻塞的线程会被唤醒,否则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;
            }
        }
    }

3.2 构造方法

初始化Sync属性,同时设置锁已被获取的个数。

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

3.3 await方法

acquireSharedInterruptibly()会先调用重写的tryAcquireShared()来尝试获取锁,若锁的数量>0,会阻塞在该方法,直到锁的数量=0将当前线程从该方法唤醒。

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

3.4 countDown方法

countDown()会尝试调用重写的tryReleaseShared()来释放锁,只有getCount() == 0时返回true,进而执行doReleaseShared()来将阻塞的线程唤醒。

    public void countDown() {
        sync.releaseShared(1);
    }

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

4 一次性使用

从上面介绍可知,要想使用CountDownLatch完成相关功能,首先要在构造时传入一个int值,即Sync同步器锁的状态,当功能实现后,Sync锁的状态肯定是0,否则线程会一直阻塞。CountDownLatch中除了构造方法中可设置Sync锁的状态外没有提供其它任何方法来设置,加上Sync属性是private的,所以无法从外部访问。

因此,在使用CountDownLatch后,建议将指向CountDownLatch的引用设为null,这样方便GC。

5 阿里面试题

1.countdowlatch和cyclicbarrier的内部原理和用法,以及相互之间的差别(比如countdownlatch的await方法和countDown是怎么实现的)。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值