并发源码解析(五)--CountDownLatch

一、CountDownLatch

CountDownLatch适用于需要主线程开启多个线程去并行执行,并且主线程需要等待所有子线程执行完毕后再进行汇总的场景。

使用CountDownLatch比较简单方便,可以先看下下面的实例代码:

package com.cf.test.lock;

import java.util.concurrent.CountDownLatch;

public class CountDownLatchDemo {

    public static void main(String[] args) throws InterruptedException {
        CountDownLatch count = new CountDownLatch(2);

        new Thread(() -> {
            System.out.println("线程1启动了");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程1结束");
            count.countDown();
        },"t1").start();

        new Thread(() -> {
            System.out.println("线程2启动了");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程2结束了");
            count.countDown();
        },"t2").start();

        System.out.println("等待线程1.2结束");
        count.await();
        System.out.println("线程1,2结束了");

    }

}

结果:
在这里插入图片描述

二、源码解析

2.1 继承关系

在这里插入图片描述
比较孤单

在这里插入图片描述
有一个Sync内部类,继承AQS
在这里插入图片描述

2.2 构造方法

只有一个带参构造

public CountDownLatch(int count) {
	//小于0抛异常
    if (count < 0) throw new IllegalArgumentException("count < 0");
    //创建一个Sync对象并传入count参数
    this.sync = new Sync(count);
}

再看一下内部类Sync

Sync(int count) {
	//设置同步状态为count
    setState(count);
}

可以看出Sync类将穿入的count设为了同步状态值,此时调用await()方法就会阻塞线程,子线程调用countDown()方法让state减1,所有子线程调用完之后,state=0,这是await方法就会返回。

2.3 常用方法

2.3.1 await()方法

public void await() throws InterruptedException {
	//调用了AQS的内部方法
    sync.acquireSharedInterruptibly(1);
}

await()方法调用了AQS中的acquireSharedInterruptibly方法

//获取共享资源时可被中断
public final void acquireSharedInterruptibly(int arg)
        throws InterruptedException {
    //如果线程中断则抛出异常
    if (Thread.interrupted())
        throw new InterruptedException();
    //tryAcquireShared是子类实现的,后面看
    //这里是如果state不为0,进入if语句
    if (tryAcquireShared(arg) < 0)
    	//进入AQS的等待队列
        doAcquireSharedInterruptibly(arg);
}

Sync中的tryAcquireShared方法

//只是判断state是否为0
protected int tryAcquireShared(int acquires) {
	//state是否等于0,为0返回1,不为0返回-1
    return (getState() == 0) ? 1 : -1;
}

2.3.2 countDown()方法

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

countDown调用了AQS的releaseShared方法,而tryReleaseShared也是由子类实现的,在这里由Sync实现

public final boolean releaseShared(int arg) {
	//是否state已经到0
    if (tryReleaseShared(arg)) {
    	//释放锁,激活阻塞线程
        doReleaseShared();
        return true;
    }
    return false;
}

子类实现的tryReleaseShared方法

   protected boolean tryReleaseShared(int releases) {
        // Decrement count; signal when transition to zero
        for (;;) {
            int c = getState();
            //如果c == 0,不能再减了
            if (c == 0)
                return false;
            //原子性设置c - 1
            int nextc = c-1;
            if (compareAndSetState(c, nextc))
                return nextc == 0;
        }
    }

三、总结

CountDownLatch的使用非常灵活和方便。底层是AQS,使用AQS的state来存放计数器。调用countDown()方法来原子性递减state的值,await()后被放入AQS的阻塞队列等待state为0后再激活返回。

在给个吃鸡:

package com.cf.test.lock;

import java.util.concurrent.CountDownLatch;

public class Player extends Thread {

    private static int id = 1;
    private CountDownLatch countDownLatch;

    public Player(CountDownLatch countDownLatch) {
        this.countDownLatch = countDownLatch;
    }

    @Override
    public void run() {
        System.out.println("玩家" + (id++) + "加入战斗");
        countDownLatch.countDown();
    }


    public static void main(String[] args) throws InterruptedException {
        int size = 4;
        CountDownLatch count = new CountDownLatch(size);
        for(int i = 0 ; i < size; i++){
            new Player(count).start();
        }
        count.await();
        System.out.println("训练即将开始");
        System.out.println("大吉大利,今晚吃鸡");
    }

}

结果:

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值