CountDownLatch的理解使用

一、介绍

CountDownLatch是jdk1.5中引入的一种同步辅助,允许一个或者多个线程等待,知道在其他线程中执行的一组操作完成。

CountDownLatch使用给定的计数器进行初始化,由于调用了countDown方法,await方法会阻塞,知道当前计数器达到为零,然后释放所有等待线程,并且任何后续的await方法都会立即返回,由于在初始化时指定了计数数量,所以无法重置技术,如果需要重置计数,需要使用CyclicBarrier.

计数为1的CountDownLatch可用于简单的锁,调用await的所有线程在等待,直到它被调用countDown的线程打开,初始化为N的线程可用于使一个主线程等待,直到N个线程完成操作执行完成,或者某个操作已完成N次。使用场景最多的可能是主线程等待所有子线程是否执行完毕。

简单来说,CountDownLatch类是一个基于AbstractQueuedSynchronizer(AQS)实现的计数器,可以设置初始化线程数(设置完后不可改变),在子线程结束时调用countDown()方法可以使计数数量减1,当所有线程结束,即最终getCount()为0时,会调用CountDownLatch的成员方法wait()的线程就会取消BLOCKED阻塞状态,进去RUNNABLE从而继续执行。

二、使用

下面来看源码里举的例子,我理解的是四个搬运工装车

import java.util.concurrent.CountDownLatch;

/**
* 司机
* @author
*/
public class Driver {

public static void main(String[] args) throws InterruptedException {
int N = 4;

// 开始装车信号
CountDownLatch startSignal = new CountDownLatch(1);

// 装车完毕信号
CountDownLatch doneSignal = new CountDownLatch(N);

// 初始化四个搬运工
for (int i = 0; i < N; ++i) {
new Thread(new WorkerRunnable(startSignal, doneSignal), "Worker-" + i).start();
}

// 司机做准备工作,比如把车开到指定地方
doSomethingElse();

// 准备完了,发信号让开始搬运
startSignal.countDown();

// 司机等待所有搬运工完成
doneSignal.await();

// 完成
doSomethingElse2();
}

static void doSomethingElse() throws InterruptedException {
Thread.sleep(1000);
System.out.println("司机准备工作完成");
}

static void doSomethingElse2() throws InterruptedException {
Thread.sleep(1000);
System.out.println("滴,滴滴~ 老司机发车了");
}
}
import java.util.Random;
import java.util.concurrent.CountDownLatch;

/**
 * 搬运工
 * @author 15641
 */
public class WorkerRunnable implements Runnable{

    private final CountDownLatch startSignal;

    private final CountDownLatch doneSignal;

    public WorkerRunnable(CountDownLatch startSignal, CountDownLatch doneSignal) {
        this.startSignal = startSignal;
        this.doneSignal = doneSignal;
    }

    @Override
    public void run() {
        try {
            // 搬运工等待准备工作完成
            startSignal.await();

            // 开始工作
            doWork();

            // 装完之后给司机发个信号
            doneSignal.countDown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    void doWork() throws InterruptedException {
        // 模拟每个工人的装车速度
        Random random = new Random();
        Thread.sleep((long)(random.nextDouble() * 5000));

        System.out.println(Thread.currentThread().getName() + "装完了");
    }
}

执行结果为

CountDownLatch的理解使用

上述例子是多个主线程等待多个线程全部执行完,第二个例子是使用线程池用一个Runnable重复执行每一部分

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

/**
 * @author 15641
 */
public class Driver2 {

    public static void main(String[] args) throws InterruptedException {
        int N = 4;
        CountDownLatch doneSignal = new CountDownLatch(N);
        ExecutorService service = Executors.newFixedThreadPool(N);

        for (int i = 0; i < N; ++i) {
            service.submit(new WorkerRunnable2(doneSignal, i));
        }

        doneSignal.await();
    }
}
import java.util.Random;
import java.util.concurrent.CountDownLatch;

/**
 * @author 15641
 */
public class WorkerRunnable2 implements Runnable{

    private final CountDownLatch doneSingal;

    private final int i;

    public WorkerRunnable2(CountDownLatch doneSingal, int i) {
        this.doneSingal = doneSingal;
        this.i = i;
    }

    @Override
    public void run() {
        try {
            doWork(i);
            doneSingal.countDown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    void doWork(int i) throws InterruptedException {
        // 模拟每个工人的装车速度
        Random random = new Random();
        Thread.sleep((long)(random.nextDouble() * 8000));

        System.out.println("任务" + i + "完成了");
    }
}

 

上一篇:Android 子线程和主线程同步的两种方法


下一篇:Java基础知识12--使用CountDownLatch实现模拟多线程并发请求