一、CountDownLatch
1、定义
CountDownLatch是一个同步工具类,它通过一个计数器来实现的,初始值为线程的数量。每当一个线程完成了自己的任务,计数器的值就相应得减1。当计数器到达0时,表示所有的线程都已执行完毕,然后在等待的线程就可以恢复执行任务。
2、方法详解
CountDownLatch(int count):count为计数器的初始值(一般需要多少个线程执行,count就设为几)。
countDown():每调用一次计数器值-1,直到count被减为0,代表所有线程全部执行完毕。
getCount():获取当前计数器的值。
await():等待计数器变为0,即等待所有异步线程执行完毕。
boolean await(long timeout, TimeUnit unit): 此方法与await()区别:
①此方法至多会等待指定的时间,超时后会自动唤醒,若 timeout 小于等于零,则不会等待
②boolean 类型返回值:若计数器变为零了,则返回 true;若指定的等待时间过去了,则返回 false
3、代码案例
(1)教室关门案例,main最后执行
import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 1; i <= 6; i++) {
new Thread(() -> {
countDownLatch.countDown();
System.out.println(Thread.currentThread().getName() + "已退出");
}, String.valueOf(i)).start();
}
countDownLatch.await();
System.out.println(Thread.currentThread().getName() + "已退出");
}
}
输出结果:
1已退出
5已退出
4已退出
3已退出
6已退出
2已退出
main已退出
(2)enum 枚举使用的小技巧
import lombok.Getter;
public enum CountryEnum {
ONE(1, "齐"), TWO(2, "楚"), THREE(3, "燕"), FOUR(4, "赵"), FIVE(5, "魏"), SIX(6, "韩");
@Getter
private Integer retCode;
@Getter
private String resMessage;
CountryEnum(Integer retCode, String resMessage) {
this.retCode = retCode;
this.resMessage = resMessage;
}
public static CountryEnum forEach_CountryEnum(int index){
CountryEnum[] myArray = CountryEnum.values();
for (CountryEnum element : myArray) {
if (index == element.getRetCode()){
return element;
}
}
return null;
}
}
import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 1; i <= 6; i++) {
new Thread(() -> {
countDownLatch.countDown();
System.out.println(Thread.currentThread().getName() + " 国被灭");
}, CountryEnum.forEach_CountryEnum(i).getResMessage()).start();
}
countDownLatch.await();
System.out.println(Thread.currentThread().getName() + "秦统一");
}
}
输出结果:
齐 国被灭
魏 国被灭
赵 国被灭
燕 国被灭
楚 国被灭
韩 国被灭
main秦统一
二、CyclicBarrier
1、定义
CyclicBarrier 是另外一种多线程并发控制工具。和 CountDownLatch 非常类似,它也可以实现线程间的计数等待,但它的功能比 CountDownLatch 更加复杂且强大。
CyclicBarrier 可以理解为循环栅栏。
栅栏就是一种障碍物,比如,通常在私人宅邸的周围就可以围上一圈栅栏,阻止闲杂人等入内。
这里当然就是用来阻止线程继续执行,要求线程在栅栏外等待。
前面 Cyclic 意为循环,也就是说这个计数器可以反复使用。
比如,我们将计数器设置为 10 ,那么湊齐第一批 10 个线程后,计数器就会归零,接着凑齐下一批 10 个线程,这就是循环栅栏内在的含义。
2、代码案例
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierDemo {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
System.out.println("收集到7颗");
});
for (int i = 1; i <= 7; i++) {
int temp = i;
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " 收集到第" + temp + "颗");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}, String.valueOf(i)).start();
}
}
}
输出结果:
1 收集到第1颗
6 收集到第6颗
7 收集到第7颗
4 收集到第4颗
5 收集到第5颗
2 收集到第2颗
3 收集到第3颗
收集到7颗
3、CyclicBarrier与CountDownLacth对比
CyclicBarrier
可以循环使用
工作线程之间必须等到同一个点才能执行
CountDownLacth
CountDownLacth 不能reset
工作线程之间彼此不关心
三、Semaphore
1、定义
Semaphore的基本使用场景是限制一定数量的线程能够去执行。
举个简单的例子:一个单向隧道能同时容纳10个小汽车或5个卡车通过(1个卡车等效与2个小汽车),而隧道入口记录着当前已经在隧道内的汽车等效比重。比如1个小汽车和1个卡车, 则隧道入口显示3。若隧道入口显示10表示已经满了。当汽车驶出隧道之后, 隧道入口显示的数字则会相应的减小于这个示例相符合场景非常适合用信号量。
Semaphore在构造的时候可以传入一个int,表示有多少许可(permit)。线程获取锁的时候,要告诉信号量使用多少许可(类比与小汽车和卡车),当线程要使用的许可不足时,则调用的线程则会被阻塞。
2、代码案例
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class SemaphoreDemo {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(3); // 模拟3个停车位
for (int i = 1; i <= 6; i++) { // 模拟6辆车
new Thread(() -> {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + " 抢到车位");
// 线程暂停3s
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 停车3秒,离开车位");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
}, String.valueOf(i)).start();
}
}
}
输出结果:
2 抢到车位
3 抢到车位
4 抢到车位
2 停车3秒,离开车位
3 停车3秒,离开车位
5 抢到车位
1 抢到车位
4 停车3秒,离开车位
6 抢到车位
1 停车3秒,离开车位
5 停车3秒,离开车位
6 停车3秒,离开车位