一 CountDownLatch
倒数器:当指定值减到0,指定线程开始执行。
CountDownLatch类可以设置一个计数器,让一些线程阻塞直到另外一些线程完成后才被唤醒。
CountDownLatch主要有两个方法,当一个或多个线程调用CountDownLatch.await()方法时,调用线程会被阻塞。其他线程调用countDown方法计数器减一(调用CountDownLatch.countDown()方法时线程不会阻塞),当计数器的值变为0,调用await方法被阻塞的线程才会被唤醒,继续执行。
1.1 案例1:教室关灯
public class CountDownLatchDemo1 {public static void main(String[] args) throws Exception {closeDoor();}/*** 关门案例*/private static void closeDoor() throws InterruptedException {CountDownLatch countDownLatch = new CountDownLatch(6); // 定义了6次,需要删减6次for (int i = 1; i <= 6; i++) {new Thread(() -> {System.out.println("学生" + Thread.currentThread().getName() + "\t" + "上完自习");countDownLatch.countDown(); //减一}, String.valueOf(i)).start();}countDownLatch.await(); // 线程阻塞,直到计数减为0System.out.println(Thread.currentThread().getName() + "\t班长锁门离开教室");}}
1.2 案例2:秦灭六国
public class CountDownLatchDemo2 {public static void main(String[] args) throws Exception {sixCountry();}/*** 秦灭六国 一统华夏*/private static void sixCountry() 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(i).getName()).start();}countDownLatch.await();System.out.println("秦统一");}}enum CountryEnum {ONE(1, "齐"),TWO(2, "楚"),THREE(3, "燕"),FOUR(4, "赵"),FIVE(5, "魏"),SIX(6, "韩");CountryEnum(Integer code, String name) {this.code = code;this.name = name;}@Getterprivate Integer code;@Getterprivate String name;public static CountryEnum forEach(int index) {CountryEnum[] countryEnums = CountryEnum.values();for (CountryEnum countryEnum : countryEnums) {if (index == countryEnum.getCode()) {return countryEnum;}}return null;}}
1.3 案例3:倒计时
public static void test1() {CountDownLatch countDownLatch = new CountDownLatch(3);new Thread(() -> {try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("数据1初始化");countDownLatch.countDown();try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("数据2初始化");countDownLatch.countDown();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("数据3初始化");countDownLatch.countDown();}).start();try {countDownLatch.await();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("数据全部完成");}
二 CyclicBarrier
计数器:当线程运行数达到一定数量,就开放屏障
CyclicBarrier的字面意思是可循环(Cyclic) 使用的屏障(barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫做同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活,线程进入屏障通过CyclicBarrier的await()方法。
当拥有指定数量的线程执行了 cyclicBarrier.await 方法时,cyclicBarrier中的线程和其他线程才会继续执行。
2.1 案例:召唤神龙
当只有凑齐 七 颗龙珠,才能最后成功召唤神龙
public class CyclicBarrierDemo {public static void main(String[] args) {CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {// 当所有的线程达到屏障,才会执行当前方法System.out.println("召唤神龙");});for (int i = 1; i <= 7; i++) {final int temp = i;new Thread(() -> {System.out.println(Thread.currentThread().getName() + "\t 收集到第" + temp + "颗龙珠");try {cyclicBarrier.await();} catch (InterruptedException e) {e.printStackTrace();} catch (BrokenBarrierException e) {e.printStackTrace();}}, String.valueOf(i)).start();}}}
三 Semaphore
信号量(Semaphore)和CountDownLatch一样,内部维护一个核心属性sync,通过AQS的共享锁机制实现。在内部维护着指定数量的许可证,并根据许可证数量进行颁发给尝试获取它的线程
信号量的主要用户两个目的:
1)一个是用于多个共享资源的相互排斥使用
2)另一个用于并发资源数的控制
3.1 核心方法
- Semaphore(int premits,boolean fair): 构造器方法。permits为信号量初始化数量,第二个参数fair可以设置是否需要公平策略,如果传入true,那么Semaphore会把等待的线程放入FIFO队列中,以便许可证被释放后,可以分配给等待时间最长的线程
- Semaphore(int n):构造器,并初始化设置许可证数量。
- acquire(): 试图获取许可证,如果当前没有可用的,就会进入阻塞等待状态
- tryAcquire(): 试图获取许可证,如果是否能够获取,都不会进入阻塞
- tryAcquire(long timeout, TimeUnit unit): 和tryAcquire一样,只是多了一个超时时间,等待指定时间还获取不到许可证,就会停止等待
- availablePermits():返回此信号量当前可用的许可证数量。此方法常用于调试和测试目的。
- release(): 释放一个许可证
- release(int permits): 释放指定数量的许可证。
3.2 案例:抢车位
public class SemaphoreDemo {public static void main(String[] args) {// 1、模拟3个停车位Semaphore semaphore = new Semaphore(3);// 模拟6部汽车for (int i = 1; i <= 6; i++) {new Thread(() -> {try {// 2、抢到资源semaphore.acquire();System.out.println(Thread.currentThread().getName() + "\t抢到车位");try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "\t 停3秒离开车位");} catch (InterruptedException e) {e.printStackTrace();} finally {// 3、释放资源semaphore.release();}}, String.valueOf(i)).start();}}}
