Java多线程系列(九)—CountDownLatch源码分析

Java多线程系列(九)—CountDownLatch源码分析

CountDownLatch是多线程辅助类,主要用作栅栏功能,也就是让一个线程等待其他线程完成之后再执行;

个人主页:tuzhenyu’s page
原文地址:Java多线程系列(九)—CountDownLatch源码分析

(0) CountDownLatch的实例

class MyThread2 extends Thread{
    private CountDownLatch cd;
    public MyThread2(String name,CountDownLatch cd){
        super(name);
        this.cd = cd;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(1000);
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+" is countDownLatch");
        cd.countDown();
    }
}
public class CountDownLatchDemo {
    public static void main(String[] args) {
        CountDownLatch cd = new CountDownLatch(2);
        MyThread2 t1 = new MyThread2("t1",cd);
        MyThread2 t2 = new MyThread2("t2",cd);
        t1.start();
        t2.start();
        try {
            cd.await();
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println("main function is going to continue");
    }
}

输出结果


t1 is countDownLatch

t2 is countDownLatch

main function is going to continue

(1)countdownlatch作用和应用场景

  • CountDownLatch这个类能够使一个线程等待其他线程完成各自的工作后再执行。

  • CountDownLatch是通过一个计数器来实现的,计数器的初始值为线程的数量。每当一个线程完成了自己的任务后,计数器的值就会减1。当计数器值到达0时,它表示所有的线程已经完成了任务,然后在闭锁await()上等待的线程就可以恢复执行任务。

  • CountDownLatch的一个非常典型的应用场景是:有一个任务想要往下执行,但必须要等到其他的任务执行完毕后才可以继续往下执行。假如我们这个想要继续往下执行的任务调用一个CountDownLatch对象的await()方法,其他的任务执行完自己的任务后调用同一个CountDownLatch对象上的countDown()方法,这个调用await()方法的任务将一直阻塞等待,直到这个CountDownLatch对象的计数值减到0为止。

  • CountDownLatch的关键在于多线程调用CountDown()方法,而CountDown()方法是原子操作

(2)countdownlatch主要函数


CountDownLatch(int count)

构造一个用给定计数初始化的 CountDownLatch。



// 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。

void await()

// 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间。

boolean await(long timeout, TimeUnit unit)

// 递减锁存器的计数,如果计数到达零,则释放所有等待的线程。

void countDown()

// 返回当前计数。

long getCount()

// 返回标识此锁存器及其状态的字符串。

String toString()

(3)CountDownLatch语义实现

1. CountDownLatch类的结构

  • CountDownLatch类的底层实现依赖于内部类Sync,Sync继承于同步器AQS;

这里写图片描述

2. CountDownLatch类的初始化

  • 初始化countdownlatch线程计数器实例,设定线程数目
CountDownLatch latch = new CountDownLatch(3);     //设置线程计数器的初始值为3
  • CountDownLatch构造器函数传值设置AQS的state值,进而通过共享锁的个数实现线程计数器
public CountDownLatch(int count) {
    if (count < 0) throw new IllegalArgumentException("count < 0");
    this.sync = new Sync(count);
}
  • Sync类的初始化传值,设定Sync类的state的值为count
Sync(int count) {
    setState(count);
}
protected final void setState(long newState) {
    thsi.state = newState;
}

3. CountDownLatch通过await()方法阻塞需要等待的线程

  • await()方法调用流程

这里写图片描述
- 调用await()方法阻塞当前线程

countDownLatch.await()
  • await()方法调用Sync类的acquireSharedInterruptibly()方法实现线程的阻塞;
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);
}
  • 调用tryAcquireShared()判断到达栅栏情况,如果其他线程都已经执行完到达栅栏则返回1,否则返回-1
protected int tryAcquireShared(int acquires) {
    return (getState() == 0) ? 1 : -1;
}
  • 如果还有线程未到达栅栏则调用doAcquireSharedInterruptibly()让当前线程一直等待,直到获取成功
private void doAcquireSharedInterruptibly(long arg)

    throws InterruptedException {

    // 创建"当前线程"的Node节点,且Node中记录的锁是"共享锁"类型;并将该节点添加到CLH队列末尾。

    final Node node = addWaiter(Node.SHARED);

    boolean failed = true;

    try {

        for (;;) {

            // 获取上一个节点。

            // 如果上一节点是CLH队列的表头,则调用tryAcquireShared()判断到达栅栏情况

            final Node p = node.predecessor();

            if (p == head) {

                long r = tryAcquireShared(arg);

                if (r >= 0) {

                    setHeadAndPropagate(node, r);

                    p.next = null; // help GC

                    failed = false;

                    return;

                }

            }

            // (上一节点不是CLH队列的表头) 当前线程一直等待,直到被唤醒

            // 如果线程在等待过程中被中断过,则再次中断该线程(还原之前的中断状态)。

            if (shouldParkAfterFailedAcquire(p, node) &&

                parkAndCheckInterrupt())

                throw new InterruptedException();

        }

    } finally {

        if (failed)

            cancelAcquire(node);

    }

}

4. CountDownLatch通过countDown()方法将栅栏状态state减一

  • countDown()方法执行流程

这里写图片描述

  • 调用countDown()方法将栅栏状态state减一
countDownLatch.countDown()
  • countDown()方法调用Sync类的releaseShared()方法
public void countDown() {
    sync.releaseShared(1);
}
  • 调用releaseShared()方法,判断当前state状态,如果栅栏state已经为0,则调用doRelease()方法释放阻塞的线程
public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {
        doReleaseShared();
        return true;
    }
    return false;
}
  • 调用tryReleaseShared()方法判断栅栏state状态
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;
    }
}
  • 调用doReleaseShared()方法唤醒所有阻塞的线程
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;
    }
}

5. CountDownLatch和CyclicBarrier区别

  • CountDownLatch是一个线程等待多个线程完成某个事情之后才会执行;CyclicBarrier是多个线程相互等待,只有多个线程都到达同步点线程才能继续运行;区别在于一个线程等待和多个线程相互等待;

  • CyclicBarrier初始时还可带一个Runnable的参数,此Runnable任务在CyclicBarrier的数目达到后,优先于其它被唤醒的线程提前被执行。

  • 在底层实现上,CyclicBarrier的语义实现是基于互斥锁ReentrantLock和其对应的Condition;CountDownLatch语义的实现是通过内部类Sync;

总结

  • CountDownLatch实现了栅栏功能,也就是一个线程等待其他线程运行到栅栏点后才继续运行,主要通过await()方法和countDown()方法实现;

  • 当一个线程调用await()方法时会判断栅栏state的状态,如果不为0则阻塞加入同步队列,其他线程执行countDown()方法时会将state减一并判断state的值是否为0,如果为0则唤醒所有阻塞在同步队列中的线程;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值