Java多线程系列-CountDownLatch

来源:互联网 发布:计算机编程历史 编辑:程序博客网 时间:2024/06/07 21:58

概述

JDK中提供了一些用于线程之间协同等待的工具类,CountDownLatch和CyclicBarrier就是最典型的两个线程同步辅助类。下面分别详细介绍这两个类,以及他们之间的异同点。

CountDownLatch类

CountDownLatch顾名思义:倒计数锁存器。没错,他就是一个计数器,并且是倒着计数的。他的应用场景如下:

一个任务A,他需要等待其他的一些任务都执行完毕之后它才能执行。就比如说赛跑的时候,发令员需要等待所有运动员都准备好了才能发令,否则不被K才怪嘞!

此时CountDownLatch就可以大展身手了。

常用操作

本节介绍CountDownLatch的基本操作函数。

构造函数

函数签名如下:

1
public CountDownLatch(int count)

用一个给定的数值初始化CountDownLatch,之后计数器就从这个值开始倒计数,直到计数值达到零。

await函数

await函数用两种形式,签名分别如下:

12
public void await() throws InterruptedExceptionpublic boolean await(long timeout, TimeUnit unit)

这两个函数的作用都是让线程阻塞等待其他线程,直到CountDownLatch的计数值变为0才继续执行之后的操作。区别在于第一个函数没有等待时间限制,可以等到天荒地老,海枯石烂,第二个函数给定一个等待超时时间,超过该时间就直接放弃了,并且第二个函数具有返回值,超时时间之内CountDownLatch的值达到0就返回true,等待时间结束计数值都还没达到0就返回false。这两个操作在等待过程中如果等待的线程被中断,则会抛出InterruptedException异常。

countDown函数

这个函数用来将CountDownLatch的计数值减一,函数签名如下:

1
public void countDown()

需要说明的是,如果调用这个函数的时候CountDownLatch的计数值已经为0,那么这个函数什么也不会做。

getCount函数

该函数用来获取当前CountDownLatch的计数值,函数签名如下:

1
public void countDown()

模拟实验

理论知识讲完了,需要真枪实战地来演示一下这个类的作用,我们就以下面这个场景为例子,用CountDownLatch来实现这个需求:

有5个运动员赛跑,开跑之前,裁判需要等待5个运动员都准备好才能发令,并且5个运动员准备好之后也都需要等待裁判发令才能开跑。

首先分析一下依赖关系:

裁判发令 -> 5个运动员都准备好;
5个运动员开跑 -> 裁判发令。

好,依赖关系已经出来了,代码实现:

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
package com.winwill.test;import java.util.Random;import java.util.concurrent.CountDownLatch;/** * @author qifuguang * @date 15/8/24 23:35 */public class TestCountDownLatch {    private static final int RUNNER_NUMBER = 5; // 运动员个数    private static final Random RANDOM = new Random();    public static void main(String[] args) {        // 用于判断发令之前运动员是否已经完全进入准备状态,需要等待5个运动员,所以参数为5        CountDownLatch readyLatch = new CountDownLatch(RUNNER_NUMBER);        // 用于判断裁判是否已经发令,只需要等待一个裁判,所以参数为1        CountDownLatch startLatch = new CountDownLatch(1);        for (int i = 0; i < RUNNER_NUMBER; i++) {            Thread t = new Thread(new Runner((i + 1) + "号运动员", readyLatch, startLatch));            t.start();        }        try {            readyLatch.await();        } catch (InterruptedException e) {            e.printStackTrace();        }        startLatch.countDown();        System.out.println("裁判:所有运动员准备完毕,开始...");    }    static class Runner implements Runnable {        private CountDownLatch readyLatch;        private CountDownLatch startLatch;        private String name;        public Runner(String name, CountDownLatch readyLatch, CountDownLatch startLatch) {            this.name = name;            this.readyLatch = readyLatch;            this.startLatch = startLatch;        }        public void run() {            int readyTime = RANDOM.nextInt(1000);            System.out.println(name + ":我需要" + readyTime + "秒时间准备.");            try {                Thread.sleep(readyTime);            } catch (InterruptedException e) {                e.printStackTrace();            }            System.out.println(name + ":我已经准备完毕.");            readyLatch.countDown();            try {                startLatch.await();  // 等待裁判发开始命令            } catch (InterruptedException e) {                e.printStackTrace();            }            System.out.println(name + ":开跑...");        }    }}

运行结果如下:

1号运动员:我需要389秒时间准备.
2号运动员:我需要449秒时间准备.
3号运动员:我需要160秒时间准备.
4号运动员:我需要325秒时间准备.
5号运动员:我需要978秒时间准备.
3号运动员:我已经准备完毕.
4号运动员:我已经准备完毕.
1号运动员:我已经准备完毕.
2号运动员:我已经准备完毕.
5号运动员:我已经准备完毕.
裁判:所有运动员准备完毕,开始…
1号运动员:开跑…
5号运动员:开跑…
2号运动员:开跑…
4号运动员:开跑…
3号运动员:开跑…

可以看到,一切都是如此地完美,运动员准备好了之后裁判才发令,裁判发令之后运动员才开跑。

0 0