题目: 设计并编写一个程序, 10名选手准备就绪后, 等待一名裁判吹哨开始比赛.
1. CountDownLatch
1.1 概念
使用 CountDownLatch 可以使线程在等待其他线程完成任务之后, 再继续执行自己的任务.
通过一个计数器, 当其他线程完成了自己的任务之后, 通过计数器-1的操作, 当计数器值为0的时候, 其他线程的操作就完成了, 接下来在等待的线程, 就会恢复状态, 执行自己的任务.
1.2 基础方法
- 在初始化的时候, 通过调用一个有参的构造方法, 来设置等待的线程数.
- 通过调用
countDown()
方法, 让计数器减一
- 通过调用
await()
方法, 让线程等待其他线程操作, 直到计数器为0, 再进行自己的操作
2. 模拟实现百米赛跑
2.1 设计思路
- 这里通过一个CountDownLatch代表裁判, 初始化为1.
- 这里通过另一个CountDownLatch代表玩家, 初始化为10.
- 这里通过线程池去创建10个线程.
- 每个线程初始化的时候, 去调用裁判CountDownLatch的await()方法来进行准备, 相当于预备跑
- 当所有的线程都准备好之后, 裁判就调用countDown()方法, 吹哨开跑, 每个玩家就开始跑步.
- 他们的速度是随机的, 通过调用Random这个类来实现.
- 当玩家跑完步之后, 玩家CountDownLatch就调用countDown方法.
- 当计数器为0的时候, 比赛就结束了, 释放线程池. 结束比赛.
2.2 代码实现
package com.example.demo;
import lombok.SneakyThrows;
import java.util.Random;
import java.util.concurrent.*;
public class TEst {
// begin 代表裁判 初始为 1
private static CountDownLatch begin = new CountDownLatch(1);
// end 代表玩家 初始为 10
private static CountDownLatch end = new CountDownLatch(10);
public static void main(String[] args) throws InterruptedException {
// 首先创建线程池容量为10
ExecutorService es = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
// 创建 10 个线程
es.submit(new Runnable() {
@SneakyThrows
@Override
public void run() {
// 预备状态
System.out.println("参赛者"+Thread.currentThread().getName().substring(Thread.currentThread().getName().lastIndexOf('-')+1) + "已经准备好了");
// 等待裁判吹哨
begin.await();
// 开始跑步
System.out.println("参赛者"+Thread.currentThread().getName().substring(Thread.currentThread().getName().lastIndexOf('-')+1) + "开始跑步");
// 等待时间就是跑步的总时间
Thread.sleep(new Random().nextInt(10) * 10000);
// 跑步结束, 跑完了
System.out.println("参赛者"+Thread.currentThread().getName().substring(Thread.currentThread().getName().lastIndexOf('-')+1)+ "到达终点");
// 跑到重点, 计数器就减一
end.countDown();
}
});
}
// 等待 5s 就开始吹哨
Thread.sleep(5000);
System.out.println("开始比赛");
// 裁判吹哨, 计数器减一
begin.countDown();
// 等待所有玩家到达终点
end.await();
System.out.println("比赛结束");
// 关闭线程池
es.shutdown();
}
}
2.3 运行结果