(之前掘金的“程序员七猫”账号找不到了,只好重新开了个,会把前2篇文章都搬家到这里到)
本次漫画改编自鸿洋的《Java 并发专题 :闭锁 CountDownLatch 之一家人一起吃个饭》 https://blog.csdn.net/lmj623565791/article/details/26626391
谢谢鸿洋大神的支持!
某天,小猫咪一家决定要去饭馆吃饭,到了快下班的时候,family·f3群里开始聊起天来
以下是来自这位可怜的猫咪爸爸的吐槽
下班到了(欢呼!),大家都开始出发去饭店了
public class Test1 { /** * 模拟爸爸去饭店 */ public static void fatherToRes() { System.out.println("爸爸步行去饭店需要3小时。"); } /** * 模拟妈妈去饭店 */ public static void motherToRes() { System.out.println("妈妈挤公交去饭店需要2小时。"); } /** * 模拟我去饭店 */ public static void meToRes() { System.out.println("我乘地铁去饭店需要1小时。"); } /** * 模拟一家人到齐了 */ public static void togetherToEat() { System.out.println("一家人到齐了,开始吃饭"); } public static void main(String[] args) { fatherToRes(); motherToRes(); meToRes(); togetherToEat(); } }
输出结果
爸爸步行去饭店需要3小时。 妈妈挤公交去饭店需要2小时。 我乘地铁去饭店需要1小时。 一家人到齐了,开始吃饭
!停停停 !是不是哪里不对了,为什么是猫咪爸爸走完3小时,妈妈再去坐公交车2小时,然后才等到小猫咪乘地铁1小时,这整整花了6小时,那还吃什么饭,直接买份夜宵算了!
猫咪爸爸、妈妈、小猫咪都是各自按下班时间出发的,属于并发的,所以我们换个场景
开启爸爸走路、妈妈坐公交、小猫咪坐地铁三个线程,如下
public static void main(String[] args) { new Thread() { public void run() { fatherToRes(); }; }.start(); new Thread() { public void run() { motherToRes(); }; }.start(); new Thread() { public void run() { meToRes(); }; }.start(); togetherToEat(); }
输出结果
一家人到齐了,开始吃饭 我乘地铁去饭店需要1小时。 妈妈挤公交去饭店需要2小时。 爸爸步行去饭店需要3小时。
喵?喵?喵?!
开启了线程并发了,但是貌似也不对啊,一家三都还没到饭馆,怎么能先吃起饭来?
于是再换一个方法
private static volatile int i = 3; public static void main(String[] args) { new Thread() { public void run() { fatherToRes(); i--; }; }.start(); new Thread() { public void run() { motherToRes(); i--; }; }.start(); new Thread() { public void run() { meToRes(); i--; }; }.start(); while (i != 0); togetherToEat(); }
关键修饰符 volatile
volatile作用:当多个线程操作同一个变量时,用于保证变量修改对于其他线程的可见性。但是volatile不能保证原子性,而i--不是原子操作。所以建议正常使用同步块或者AtomicLong.decrementAndGet()实现--。
马上来~
private static CountDownLatch latch = new CountDownLatch(3); public static void main(String[] args) throws InterruptedException { new Thread() { public void run() { fatherToRes(); latch.countDown(); }; }.start(); new Thread() { public void run() { motherToRes(); latch.countDown(); }; }.start(); new Thread() { public void run() { meToRes(); latch.countDown(); }; }.start(); latch.await(); togetherToEat(); }
输出结果
我乘地铁去饭店需要1小时。 妈妈挤公交去饭店需要2小时。 爸爸步行去饭店需要3小时。 一家人到齐了,开始吃饭
CountDowmLatch是一种灵活的闭锁实现,包含一个计数器,该计算器初始化为一个正数,表示需要等待事件的数量。countDown方法递减计数器,表示有一个事件发生,而await方法等待计数器到达0,表示所有需要等待的事情都已经完成,那么就可以继续执行当前线程了。
CountDownLatch的使用场景
1、例如上例中所有人都到达饭店然后吃饭
2、某个操作需要的资源初始化完毕
3、某个服务依赖的线程全部开启等等...
上烤鱼喽~
依然感谢支持我的小伙伴们以及网友们~
您的关注与支持是我最大的动力~
本人公众号:程序员七猫
欢迎关注点赞以及提建议