JDK中提供了一些用于线程之间协同等待的工具类,CountDownLatch和CyclicBarrier就是最典型的两个线程同步辅助类。下面分别详细介绍这两个类,以及他们之间的异同点。
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号运动员:开跑…
可以看到,一切都是如此地完美,运动员准备好了之后裁判才发令,裁判发令之后运动员才开跑。
新闻热点
疑难解答