CountDownLatch

目录

CountDownLatch介绍

CountDownLatch原理

CountDownLatch 内部结构

 

CountDownLatch介绍

CountDownLatch是一个同步工具类,它允许一个或多个线程一直等待,直到其他线程执行完后再执行。

CountDownLatch是在java1.5被引入的,跟它一起被引入的并发工具类还有CyclicBarrier、Semaphore、ConcurrentHashMap和BlockingQueue,它们都存在于java.util.concurrent包下

CountDownLatch原理

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

注:这是一个一次性操作 - 计数无法重置。 如果你需要一个重置的版本计数,考虑使用CyclicBarrier。

CountDownLatch原理示意图

CountDownLatch 内部结构

CountDownLatch_第1张图片

1.Sync 是一个静态内部类 继承 AbstractQueuedSynchronizer

 /**
     * CountDownLatch的同步控制器
     * 使用AQS状态表示计数。
     */
    private static final class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 4982264981922014374L;

        //构造方法 CountDownLatch 的构造方法最终调用的是 Sync的构造。
        Sync(int count) {
            setState(count); //初始化count
        }

        //返回当前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;
            }
        }
    }

注:从源码可知,其底层是由AQS提供支持。AQS 见下章分解。

2. await()

            此函数将会使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。其源码如下  

    public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

   //-----------------------------------------------------------

    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        //这里可以看到 最终调用的是Sync中的 tryAcquireShared 方法 
        // return (count == 0) ? 1 : -1;
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }


//-------------------------------------------------------


    public boolean await(long timeout, TimeUnit unit)
        throws InterruptedException {
        return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
    }

3.countDown() 

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

   /**   
     * count值减 1,直到计数达到零,释放所有等待的线程 。
     *      
     *  

如果当前计数大于零,则递减。 *   如果新计数为零,则重新启用所有等待的线程 ,达到线程调度的目的。 *       *

如果当前计数等于零,则没有任何反应。 */ public void countDown() { sync.releaseShared(1); } //------------------------------------------------------------------------- /** * 此函数会以共享模式释放对象, * 并且在函数中会调用到CountDownLatch的tryReleaseShared函数, * 当且仅当新计数返回0时,会调用AQS的doReleaseShared函数,  */ public final boolean releaseShared(int arg) { if (tryReleaseShared(arg)) { doReleaseShared(); return true; } return false; }

CountDownLatch 使用示例

示例1

package com.kids.web.countdownlatch;

import java.util.concurrent.CountDownLatch;

public class Worker implements Runnable {

    private int num;

    private final CountDownLatch startSignal;
    private final CountDownLatch doneSignal;

    Worker(CountDownLatch startSignal, CountDownLatch doneSignal, int num) {
        this.startSignal = startSignal;
        this.doneSignal = doneSignal;
        this.num = num;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(2000);
            System.out.println("do startSignal.await() before");
            startSignal.await();
            System.out.println("do startSignal.await() after");

            doWork();
            doneSignal.countDown();
            System.out.println("===count =" + doneSignal.getCount() + "========== num =" + num);

        } catch (InterruptedException ex) {
        } // return;
    }

    void doWork() {
        System.out.println("do work..." + num);
    }
}
package com.kids.web.countdownlatch;

import java.util.concurrent.CountDownLatch;

public class Driver {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch startSignal = new CountDownLatch(1);
        int N = 10;
        CountDownLatch doneSignal = new CountDownLatch(N);

        for (int i = 0; i < N; ++i) // create and start threads
            new Thread(new Worker(startSignal, doneSignal,i)).start();

        doSomethingElse1();            // don't let run yet
        Thread.sleep(1000 * 5);
        startSignal.countDown();      // let all threads proceed
        doSomethingElse2();
        Thread.sleep(1000 * 5);
        doneSignal.await();           // wait for all to finish
    }

    private static void doSomethingElse1() {
        System.out.println("====================startSignal.countDown() before");
    }

    private static void doSomethingElse2() {
        System.out.println("====================startSignal.countDown() after");
    }
}

====================startSignal.countDown() before
do startSignal.await() before ----count=1 ---- num =0
do startSignal.await() before ----count=1 ---- num =2
do startSignal.await() before ----count=1 ---- num =4
do startSignal.await() before ----count=1 ---- num =1
do startSignal.await() before ----count=1 ---- num =3
====================startSignal.countDown() after
do startSignal.await() after  ----count=0 ---- num =3
do startSignal.await() after  ----count=0 ---- num =1
do startSignal.await() after  ----count=0 ---- num =4
do startSignal.await() after  ----count=0 ---- num =2
do work...2
doneSignal.countDown()===count =4========== num =2
do startSignal.await() after  ----count=0 ---- num =0
do work...0
do work...4
doneSignal.countDown()===count =2========== num =4
do work...1
doneSignal.countDown()===count =1========== num =1
do work...3
doneSignal.countDown()===count =0========== num =3
doneSignal.countDown()===count =3========== num =0

等待其他线程执行完毕

Process finished with exit code 0

说明:通过示例结果中可以看出,在startSignal调用countDown()之前程序在startSignal.await() 处堵塞,

此时startSignal的count为1。在startSignal调用countDown()之后,count =0 时 开始执行startSignal.await()之后的业务逻辑。

doneSignal调用countDown() 直到其count=0 回到主线程。等待doneSignal调用await()结束。

注意:doneSignal最终不调用await() 的话 该线程始终处于等待状态。(本人也不知具体原因,在await 方法中 如果计数为0的话并没有处理任何事情。而且通过sleep 看到只有doneSignal的计数为0后才会返回主线程执行。)

这里有明白的大神,恳请留言指导一下。

 

示例2

package com.kids.web.countdownlatch;

import java.util.concurrent.CountDownLatch;

public class Worker2 implements Runnable {

    private final CountDownLatch doneSignal;
    private final int i;

    public Worker2(CountDownLatch doneSignal, int i) {
        this.doneSignal = doneSignal;
        this.i = i;
    }

    public void run() {
        doWork(i);
        doneSignal.countDown();
        System.out.println("=============count =" + doneSignal.getCount() + "------- i =" + i);
    }

    void doWork(int i) {
        System.out.println("do work..." + i);
    }
}

 

package com.kids.web.countdownlatch;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

public class Driver2 {
    public static void main(String[] args) throws InterruptedException {
        int N = 20;
        CountDownLatch doneSignal = new CountDownLatch(N);
        Executor e = Executors.newFixedThreadPool(1);

        for (int i = 0; i < N; ++i) // create and start threads
            e.execute(new Worker2(doneSignal, i));
        doneSignal.await();           // wait for all to finish
    }

}

do work...0
=============count =19------- i =0
do work...1
=============count =18------- i =1
do work...2
=============count =17------- i =2
do work...3
=============count =16------- i =3
。。。。。。

。。。。。。

do work...18
=============count =1------- i =18
do work...19
=============count =0------- i =19

说明:这里 Executor e = Executors.newFixedThreadPool(1); 只是为了可以从打印结果中更直观的看出 count的递减。

 

参考: https://www.cnblogs.com/leesf456/p/5406191.html

 


你可能感兴趣的:(并发,java)