一、CountDownLatch(倒计时)
基本概念,是什么?
让一个线程await堵塞,直到另一些县城完成一系列操作后才被唤醒;
CountDownLatch主要有2个方法:当一个或者多个线程调用await方法的时候,调用线程将被堵塞;其他线程调用countDown方法将会将计数器减1(调用countDown方法的线程不会堵塞),当计数器变为0的时候,因调用await方法被堵塞的线程将会被唤醒,继续执行。(一个倒计数等待操作)
|
经典案例:自习室有7个人,1个班长和6个同学,现在的要求是班长必须等到其他6人陆续离开教室后,才可以关门走人 |
1、首先我们先尝试不使用CountDownLatch
package com.jiguiquan.www;
public class CountDownLatchDemo {
public static void main(String[] args) {
for (int i = 0; i < 6; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName()+"\t 上完自习,离开教室");
},String.valueOf(i)).start();
}
System.out.println(Thread.currentThread().getName()+"\t *******班长最后关门走人");
}
}
执行结果如下:

显然,班长成功将4位同学锁在了教室过夜!
2、使用CountDownLatch
package com.jiguiquan.www;
import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo {
public static void main(String[] args) throws Exception {
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 *******班长最后关门走人");
}
}
执行结果如下:

显然,现在流程很正确了;
| 另一个案例,秦灭六国,一统华夏,使用CountDownLatch和枚举Enum类实现 |
CountryEnum
package com.jiguiquan.www;
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 Integer getRetCode() {
return retCode;
}
public String getRetMessage() {
return retMessage;
}
//根据传进来的数字,获得对应的枚举
public static CountryEnum forEach_CountryEnum(int index) {
CountryEnum[] myArray = CountryEnum.values(); //枚举类型天生带着的一个自己的遍历方法;
for (CountryEnum item : myArray) {
if (index == item.getRetCode()) {
return item;
}
}
return null;
}
}
CountDownLatchDemo2
package com.jiguiquan.www;
import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo2 {
public static void main(String[] args) throws Exception {
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 1; i <= 6; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName()+"\t国 被灭");
countDownLatch.countDown();
},CountryEnum.forEach_CountryEnum(i).getRetMessage()).start();
}
countDownLatch.await();
System.out.println(Thread.currentThread().getName()+"\t 秦国统一六国");
}
}
执行结果:

二、CyclicBarrier(集齐7颗龙珠才能召唤神龙)
基本概念,是什么?
CyclicBarrier的逻辑和CountDownLatch正好相反;字面意思是可循环(Cyclic)使用的屏障(Barrier);
当一组线程达到一个屏障(也可以叫同步点)的时候会被堵塞,直到最后一个线程到达屏障时候,屏障才会打开,所有被屏障拦截的线程才会继续干活;
初始值为0,向上增加到预期时候,才可以执行;
线程进入屏障通过CyclicBarrier的await()方法实现;
| 经典案例:收集7颗龙珠召唤神龙 |
package com.jiguiquan.www;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierDemo {
public static void main(String[] args) {
//任何接口,如果只包含唯一一个抽象方法,那么它就是一个FI(function interface函数式接口)
//要求,函数式接口必须要用lambda表达式,好看
//public CyclicBarrier(int parties, Runnable barrierAction)
CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
System.out.println("召唤神龙");
});
for (int i = 1; i <= 7; i++) {
int tempInt = i;
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"\t 收集到第"+tempInt+"颗龙珠");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (BrokenBarrierException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}, String.valueOf(i)).start();
}
}
}
执行结果:

顺利召唤神龙
三、Semaphore(信号灯,技术信号量)
——停车场(计数方式不像上面2个,一个只减,一个只增,Semaphore有增有减,有减有增,用完归还)
Semaphore信号灯(计数信号量) 主要用于两个目的:
-
一是用于多个共享资源的互斥使用;
-
另一个用于并发线程数的控制;
| 经典案例:6部车进3个车位的停车场 |
package com.jiguiquan.www;
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()+"\t 抢到车位");
//暂停一会儿线程,代表车在这儿停一段时间
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName()+"\t 停车3秒钟后离开车位");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
},String.valueOf(i)).start();
}
}
}
执行结果:

很显然,先抢到车位的3部车,在使用过程中,456另外3部车在场外等待;
在前三部车停完3秒钟后,会归还车位,然后另外三部车又迅速进来;
Semaphore有借有还,这种特性在以后的线程池中使用,可以达到非常完美的效果!




1 Comment
你好