CountDownLatch 1.8 解析

CountDownLatch 的描述是“允许一个或者多个线程等待其他线程完成操作”
下面先看一个简单的例子“出门三部曲”

    public static void checkGoOut() throws InterruptedException {

        CountDownLatch countDownLatch = new CountDownLatch(3);
        new Thread(()->{
            System.out.println("检查手机");
            countDownLatch.countDown();
            System.out.println("检查钱包");
            countDownLatch.countDown();
            System.out.println("检查钥匙");
            countDownLatch.countDown();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        countDownLatch.await();
        System.out.println("检查完毕,可以出门了");
    }

检查手机
检查钱包
检查钥匙
检查完毕,可以出门了

以上是个很典型的例子 “一个线程阻塞等待另一个线程完成之后再继续进行”

CountDownLatch可以认为是一个步骤计数器,当所有当步骤都完成计数为0的时候,被阻塞的线程就会被唤醒

CountDownLatch的类结构,通过内部类Sync继承AQS
在这里插入图片描述

countDownLatch的主要方法

//构造方法,设置初始步骤计数,不能重复设置,而且计数只减不增
public CountDownLatch(int count)

//使当前线程等待直到计数到零为止,相应中断
public void await() throws InterruptedException

//计数减1,如果减到0,释放等待的线程
public void countDown()

//带超时机制的await,正常唤醒返回true,超时返回false,相应中断
public boolean await(long timeout, TimeUnit unit)throws InterruptedException

接下来看下源码部分,需要先了解AQS的原理

CountDownLatch构造函数,目的就是设置计数值

	//调用构造方法,设定计数值
    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        //调用内部类的构造方法
        this.sync = new Sync(count);
    }
    
    Sync(int count) {
    	//最终调用AQS中的setState,设置state的值
        setState(count);
    }	

await

	//countDownLatch采用的是共享锁,并且响应中断
	//所以调用了AQS的acquireSharedInterruptibly
	public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

	//在AQS的该方法中,调用了tryAcquireShared,之前也讲过AQS采用了模版方法
	//需要开发者重写tryAcquireShared完成获取同步状态部分
    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }
	
	//countDownLatch内部类Sync中重写了tryAcquireShared
	protected int tryAcquireShared(int acquires) {
		 //因为countDownLatch的计数是只减不增,到0就不会再变了
		 //所以这里直接判断是否为0就完事了,连同步都不需要做
		 //当state为0表示已获取同步状态,不需要进入阻塞队列
         return (getState() == 0) ? 1 : -1;
    }
    

countDown

	//相对应的需要调用AQS的releaseShared,进行锁释放
    public void countDown() {
        sync.releaseShared(1);
    }
	
	//AQS中的该方法调用了tryReleaseShared
	public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
        	//如果成功运行到这里,表示state已经为0,可以释放之前被await阻塞的线程
            doReleaseShared();
            return true;
        }
        return false;
    }
	
	//主要逻辑是线程安全的将state值减去1,如果为0,表示可以唤醒被await阻塞的线程
	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;
            }
    }

整个逻辑相当的简单,因为线程的入队、阻塞、唤醒、出队这一列过程AQS已经实现了,CountDownLatch只需要判断阻塞和唤醒的条件即可

另外CountDownLatch 和 join的功能差不多,俩者的使用场景也类似
区别在于CountDownLatch注重的是步骤,countDown也并不要求必须在某个线程内,而join注重的是线程的等待,前者比后者在应用上更灵活

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值