1.CountDownLatch
概念
让一线程阻塞直到另一些线程完成一系列操作才被唤醒。
CountDownLatch主要有两个方法(await(),countDown())。
当一个或多个线程调用await()时,调用线程会被阻塞。其它线程调用countDown()会将计数器减1(调用countDown方法的线程不会阻塞),当计数器的值变为零时,因调用await方法被阻塞的线程会被唤醒,继续执行。
场景
假设一个自习室里有7个人,其中有一个是班长,班长的主要职责就是在其它6个同学走了后,关灯,锁教室门,然后走人,因此班长是需要最后一个走的,那么有什么方法能够控制班长这个线程是最后一个执行,而其它线程是随机执行的
import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
// 计数器
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 0; i <= 6; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t 上完自习,离开教室");
countDownLatch.countDown();
}, String.valueOf(i)).start();
}
countDownLatch.await();
System.out.println(Thread.currentThread().getName() + "\t 班长最后关门");
}
}
输出结果
0 上完自习,离开教室
6 上完自习,离开教室
4 上完自习,离开教室
5 上完自习,离开教室
3 上完自习,离开教室
1 上完自习,离开教室
2 上完自习,离开教室
main 班长最后关门
不加CountDownLatch的执行结果,我们发现main线程提前已经执行完成了
输出结果
1 上完自习,离开教室
0 上完自习,离开教室
main 班长最后关门
2 上完自习,离开教室
3 上完自习,离开教室
4 上完自习,离开教室
5 上完自习,离开教室
6 上完自习,离开教室
温习枚举
枚举 + CountDownLatch
程序演示秦国统一六国
import java.util.Objects;
public enum CountryEnum {
ONE(1, "齐"), TWO(2, "楚"), THREE(3, "燕"), FOUR(4, "赵"), FIVE(5, "魏"), SIX(6, "韩");
private Integer retcode;
private String retMessage;
CountryEnum(Integer retcode, String retMessage) {
this.retcode = retcode;
this.retMessage = retMessage;
}
public static CountryEnum forEach_countryEnum(int index) {
CountryEnum[] myArray = CountryEnum.values();
for(CountryEnum ce : myArray) {
if(Objects.equals(index, ce.getRetcode())) {
return ce;
}
}
return null;
}
public Integer getRetcode() {
return retcode;
}
public void setRetcode(Integer retcode) {
this.retcode = retcode;
}
public String getRetMessage() {
return retMessage;
}
public void setRetMessage(String retMessage) {
this.retMessage = retMessage;
}
}
import java.util.concurrent.CountDownLatch;
public class UnifySixCountriesDemo {
public static void main(String[] args) throws InterruptedException {
// 计数器
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 1; i <= 6; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "国被灭了!");
countDownLatch.countDown();
}, CountryEnum.forEach_countryEnum(i).getRetMessage()).start();
}
countDownLatch.await();
System.out.println(Thread.currentThread().getName() + " 秦国统一中原。");
}
}
输出结果
齐国被灭了!
燕国被灭了!
楚国被灭了!
魏国被灭了!
韩国被灭了!
赵国被灭了!
main 秦国统一中原。
2.CyclicBarrier
概念
和CountDownLatch相反,需要集齐七颗龙珠,召唤神龙。也就是做加法,开始是0,加到某个值的时候就执行
CyclicBarrier与CountDownLatch的区别:CyclicBarrier可重复多次,而CountDownLatch只能是一次。
CyclicBarrier的字面意思就是可循环(cyclic)使用的屏障(Barrier)。它要求做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活,线程进入屏障通过CyclicBarrier的await方法
案例
集齐7个龙珠,召唤神龙的Demo,我们需要首先创建CyclicBarrier
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class SummonTheDragonDemo {
public static void main(String[] args) {
/**
* 定义一个循环屏障,参数1:需要累加的值,参数2 需要执行的方法
*/
CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
System.out.println("召唤神龙");
});
for (int i = 1; i <= 7; i++) {
final Integer tempInt = i;
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t 收集到 第" + tempInt + "颗龙珠");
try {
// 先到的被阻塞,等全部线程完成后,才能执行方法
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}, String.valueOf(i)).start();
}
}
}
输出结果
2 收集到 第2颗龙珠
6 收集到 第6颗龙珠
1 收集到 第1颗龙珠
7 收集到 第7颗龙珠
5 收集到 第5颗龙珠
4 收集到 第4颗龙珠
3 收集到 第3颗龙珠
召唤神龙
3.Semaphore
概念
信号量主要用于两个目的
①一个是用于共享资源的互斥使用
②另一个用于并发线程数的控制
正常的锁(concurrency.locks或synchronized锁)在任何时刻都只允许一个任务访问一项资源,而 Semaphore允许n个任务同时访问这个资源。
代码
我们模拟一个抢车位的场景,假设一共有6个车,3个停车位
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class SemaphoreDemo {
public static void main(String[] args) {
/**
* 初始化一个信号量为3,默认是false 非公平锁, 模拟3个停车位
*/
Semaphore semaphore = new Semaphore(3, false);
// 模拟6部车
for (int i = 0; i < 6; i++) {
new Thread(() -> {
try {
// 代表一辆车,已经占用了该车位
semaphore.acquire(); // 抢占
System.out.println(Thread.currentThread().getName() + "\t 抢到车位");
// 每个车停3秒
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "\t 离开车位");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 释放停车位
semaphore.release();
}
}, String.valueOf(i)).start();
}
}
}
输出结果
1 抢到车位
2 抢到车位
0 抢到车位
0 离开车位
2 离开车位
1 离开车位
5 抢到车位
4 抢到车位
3 抢到车位
5 离开车位
4 离开车位
3 离开车位