JUC中提供了三种常用的辅助类,通过这些辅助类可以很好的解决线程数量过多时Lock锁的频繁操作。这三种辅助类为: • CountDownLatch: 减少计数 • CyclicBarrier: 循环栅栏 • Semaphore: 信号灯
CountDownLatchCountDownLatch类可以设置一个计数器,然后通过countDown方法来进行减1的操作,使用await方法等待计数器不大于0,然后继续执行await方法之后的语句。 • CountDownLatch主要有两个方法,当一个或多个线程调用await方法时,这些线程会阻塞 • 其它线程调用countDown方法会将计数器减1(调用countDown方法的线程不会阻塞) • 当计数器的值变为0时,因await方法阻塞的线程会被唤醒,继续执行
场景: 6个同学陆续离开教室后值班同学才可以关门。
package com.dongguo.juc;
import java.util.concurrent.CountDownLatch;
/**
* @author Dongguo
* @date 2021/8/24 0024-16:03
* @description: CountDownLatch
*/
public class CountDownLatchDemo {
/**
* 6个同学陆续离开教室后值班同学才可以关门
*/
public static void main(String[] args) throws Exception {
//定义一个数值为6的计数器
CountDownLatch countDownLatch = new CountDownLatch(6);
// 创建6个同学
for (int i = 1; i {
try {
if (Thread.currentThread().getName().equals("同学6")) {
Thread.sleep(2000);
}
System.out.println(Thread.currentThread().getName() + "离开了"); //计数器减一,不会阻塞
countDownLatch.countDown();
} catch (
Exception e) {
e.printStackTrace();
}
}, "同学" + i).start();
}
//主线程await休息
System.out.println("主线程睡觉");
countDownLatch.await();//主线程被唤醒
//全部离开后自动唤醒主线程
System.out.println("全部离开了,现在的计数器为" + countDownLatch.getCount());
}
}
运行结果:
主线程睡觉
同学1离开了
同学2离开了
同学3离开了
同学4离开了
同学5离开了
同学6离开了
全部离开了,现在的计数器为0
CyclicBarrier
CyclicBarrier看英文单词可以看出大概就是循环阻塞的意思,在使用中CyclicBarrier的构造方法第一个参数是目标障碍数,每次执行CyclicBarrier一次障碍数会加一,如果达到了目标障碍数,才会执行cyclicBarrier.await()之后的语句。可以将CyclicBarrier理解为加1操作 场景: 集齐7颗龙珠就可以召唤神龙
package com.dongguo.juc;
import java.util.concurrent.CyclicBarrier;
/**
* @author Dongguo
* @date 2021/8/24 0024-16:09
* @description:
*/
public class CyclicBarrierDemo {
//定义神龙召唤需要的龙珠总数
private final static int NUMBER = 7;
//集齐7颗龙珠就可以召唤神龙
public static void main(String[] args) {
// 定义循环栅栏
CyclicBarrier cyclicBarrier = new CyclicBarrier(NUMBER, () -> {
System.out.println("集齐" + NUMBER + "颗龙珠,现在召唤神龙!!!!!!!!!");
});
//定义7个线程分别去收集龙珠
for (int i = 1; i {
try {
// if (Thread.currentThread().getName().equals("龙珠3号")) {
// System.out.println("龙珠3号抢夺战开始,孙悟空开启超级赛亚人模式!");
Thread.sleep(5000);
// System.out.println("龙珠3号抢夺战结束,孙悟空打赢了,拿到了龙珠3号!");
// } else {
System.out.println(Thread.currentThread().getName() + "收集到了!!!!");
// }
cyclicBarrier.await();
} catch (Exception e) {
e.printStackTrace();
}
}, "龙珠" + i + "号").start();
}
}
}
运行结果:
龙珠5号收集到了!!!!
龙珠3号收集到了!!!!
龙珠1号收集到了!!!!
龙珠2号收集到了!!!!
龙珠4号收集到了!!!!
龙珠7号收集到了!!!!
龙珠6号收集到了!!!!
集齐7颗龙珠,现在召唤神龙!!!!!!!!!
Semaphore
Semaphore的构造方法中传入的第一个参数是最大信号量(可以看成最大线程池),每个信号量初始化为一个最多只能分发一个许可证。使用acquire方法获得许可证,release方法释放许可 场景: 抢车位, 6部汽车3个停车位
package com.dongguo.juc;
import java.util.concurrent.Semaphore;
/**
* @author Dongguo
* @date 2021/8/24 0024-16:14
* @description: Semaphore
*/
public class SemaphoreDemo {
/**
* 抢车位, 10部汽车3个停车位
*/
public static void main(String[] args) throws Exception {
//定义3个停车位
Semaphore semaphore = new Semaphore(3);
//模拟6辆汽车停车
for (int i = 1; i {
try {
System.out.println(Thread.currentThread().getName() + "找车位ing");
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "汽车停车成功!");
Thread.sleep(3000);//停车时间
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread().getName() + "溜了溜了");
semaphore.release();
}
}, "汽车" + i).start();
}
}
}
运行结果:
汽车1找车位ing
汽车1汽车停车成功!
汽车2找车位ing
汽车2汽车停车成功!
汽车3找车位ing
汽车3汽车停车成功!
汽车4找车位ing
汽车5找车位ing
汽车6找车位ing
汽车7找车位ing
汽车8找车位ing
汽车9找车位ing
汽车10找车位ing
汽车1溜了溜了
汽车4汽车停车成功!
汽车2溜了溜了
汽车5汽车停车成功!
汽车3溜了溜了
汽车6汽车停车成功!
汽车4溜了溜了
汽车7汽车停车成功!
汽车5溜了溜了
汽车8汽车停车成功!
汽车6溜了溜了
汽车9汽车停车成功!
汽车7溜了溜了
汽车10汽车停车成功!
汽车8溜了溜了
汽车9溜了溜了
汽车10溜了溜了