作者 | 万猫学社
责编 | 王晓曼
出品 | CSDN博客

一个长头发、穿着清爽的小姐姐,拿着一个崭新的Mac笔记本向我走来,看着来势汹汹,我心想着肯定是技术大佬吧!但是我也是一个才华横溢的人,稳住我们能赢。

面试官:看你简历上有写熟悉并发编程,CountDownLatch一定用过吧,跟我说说它!

:CountDownLatch是JDK提供的一个同步工具,它可以让一个或多个线程等待,一直等到其他线程中执行完成一组操作。

面试官:CountDownLatch有哪些常用的方法?

:有CountDown方法和Await方法,CountDownLatch在初始化时,需要指定用给定一个整数作为计数器。当调用CountDown方法时,计数器会被减1;当调用Await方法时,如果计数器大于0时,线程会被阻塞,一直到计数器被CountDown方法减到0时,线程才会继续执行。计数器是无法重置的,当计数器被减到0时,调用Await方法都会直接返回。

面试官调用CountDown方法时,线程也会阻塞嘛?

:不会的,调用CountDown的线程可以继续执行,不需要等待计数器被减到0,只是调用Await方法的线程需要等待。

面试官:可以举一个使用CountDownLatch的例子吗?

:比如张三、李四和王五几个人约好去饭店一起去吃饭,这几个人都是比较绅士,要等到所有人都到齐以后才让服务员上菜。这种场景就可以用到CountDownLatch。

面试官:可以写一下吗?

我:当然可以,这是张三、李四和王五的顾客类:

package onemore.study;import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
import java.util.concurrent.CountDownLatch;public class Customer implements Runnable {private CountDownLatch latch;private String name;public Customer(CountDownLatch latch, String name) {this.latch = latch;this.name = name;}@Overridepublic void run() {try {SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");Random random = new Random();System.out.println(sdf.format(new Date()) + " " + name + "出发去饭店");Thread.sleep((long) (random.nextDouble() * 3000) + 1000);System.out.println(sdf.format(new Date()) + " " + name + "到了饭店");latch.countDown();} catch (Exception e) {e.printStackTrace();}}
}

这是服务员类:

package onemore.study;import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.CountDownLatch;public class Waitress implements Runnable {private CountDownLatch latch;private String name;public Waitress(CountDownLatch latch, String name) {this.latch = latch;this.name = name;}@Overridepublic void run() {try {SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");System.out.println(sdf.format(new Date()) + " " + name  + "等待顾客");latch.await();System.out.println(sdf.format(new Date()) + " " + name  + "开始上菜");} catch (Exception e) {e.printStackTrace();}}
}

然后,再写一个测试类,用于模拟上面所说的场景:

package onemore.study;import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;public class CountDownLatchTester {public static void main(String[] args) throws InterruptedException {CountDownLatch latch = new CountDownLatch(3);List<Thread> threads = new ArrayList<>(3);threads.add(new Thread(new Customer(latch, "张三")));threads.add(new Thread(new Customer(latch, "李四")));threads.add(new Thread(new Customer(latch, "王五")));for (Thread thread : threads) {thread.start();}Thread.sleep(100);new Thread(new Waitress(latch, "♥小芳♥")).start();}
}

运行以后的结果应该是这样的:

15:25:53.015 王五出发去饭店
15:25:53.015 李四出发去饭店
15:25:53.015 张三出发去饭店
15:25:53.062 ♥小芳♥等待顾客
15:25:54.341 张三到了饭店
15:25:54.358 李四到了饭店
15:25:56.784 王五到了饭店
15:25:56.784 ♥小芳♥开始上菜

可以看到,服务员小芳在调用Await方法时一直阻塞着,一直等到三个顾客都调用了CountDown方法才继续执行。

面试官:如果有一个顾客迟迟没到,饭店都打样了,也不能一直等啊,应该这么办?

:可以使用Await方法的另一个重载,传入等待的超时时间,比如服务员只等3秒钟,可以把服务员类中的:

latch.await();

改成:

latch.await(3, TimeUnit.SECONDS);
运行结果可能是这样的:
17:24:40.915 张三出发去饭店
17:24:40.915 李四出发去饭店
17:24:40.915 王五出发去饭店
17:24:40.948 ♥小芳♥等待顾客
17:24:43.376 李四到了饭店
17:24:43.544 王五到了饭店
17:24:43.951 ♥小芳♥开始上菜
17:24:44.762 张三到了饭店

可以看到,服务员小芳在调用Await方法时虽然被阻塞了,但是时间超过3秒后,没等顾客张三调用CountDown方法就继续执行开始上菜了。

面试官:CountDownLatch的实现原理是什么?

:CountDownLatch有一个内部类叫做Sync,它继承了AbstractQueuedSynchronizer类,其中维护了一个整数state,并且保证了修改state的可见性和原子性。

创建CountDownLatch实例时,也会创建一个Sync的实例,同时把计数器的值传给Sync实例,具体是这样的:

public CountDownLatch(int count) {if (count < 0) throw new IllegalArgumentException("count < 0");this.sync = new Sync(count);
}

CountDown方法中,只调用了Sync实例的ReleaseShared方法,具体是这样的:

public void countDown() {sync.releaseShared(1);
}

其中的ReleaseShared方法,先对计数器进行减1操作,如果减1后的计数器为0,唤醒被Await方法阻塞的所有线程,具体是这样的:

public final boolean releaseShared(int arg) {if (tryReleaseShared(arg)) { //对计数器进行减一操作doReleaseShared();//如果计数器为0,唤醒被await方法阻塞的所有线程return true;}return false;
}

其中的TryReleaseShared方法,先获取当前计数器的值,如果计数器为0时,就直接返回;如果不为0时,使用CAS方法对计数器进行减1操作,具体是这样的:

protected boolean tryReleaseShared(int releases) {for (;;) {//死循环,如果CAS操作失败就会不断继续尝试。int c = getState();//获取当前计数器的值。if (c == 0)// 计数器为0时,就直接返回。return false;int nextc = c-1;if (compareAndSetState(c, nextc))// 使用CAS方法对计数器进行减1操作return nextc == 0;//如果操作成功,返回计数器是否为0}
}

在Await方法中,只调用了Sync实例的AcquireSharedInterruptibly方法,具体是这样的:

public void await() throws InterruptedException {sync.acquireSharedInterruptibly(1);
}

其中AcquireSharedInterruptibly方法,判断计数器是否为0,如果不为0则阻塞当前线程,具体是这样的:

public final void acquireSharedInterruptibly(int arg)throws InterruptedException {if (Thread.interrupted())throw new InterruptedException();if (tryAcquireShared(arg) < 0)//判断计数器是否为0doAcquireSharedInterruptibly(arg);//如果不为0则阻塞当前线程
}

其中TryAcquireShared方法,是AbstractQueuedSynchronizer中的一个模板方法,其具体实现在Sync类中,其主要是判断计数器是否为零,如果为零则返回1,如果不为零则返回-1,具体是这样的:

protected int tryAcquireShared(int acquires) {return (getState() == 0) ? 1 : -1;
}
面试官:嗯,很不错,马上给你发Offer。

版权声明:本文为CSDN博主「万猫学社」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/heihaozi/article/details/105738230

【END】

更多精彩推荐☞芯片供应被掐断,华为能否安全渡劫?
☞OceanBase 十年:一群追梦人的成长史
☞2 年 6 个月 11 天,外包到阿里的修仙之路!| 原力计划
☞服务器软件大扫盲!
☞绝悟之后再超神,腾讯30篇论文入选AI顶会ACL
☞中本聪并没有出现,那真相是?
你点的每个“在看”,我都认真当成了喜欢

面试官跟我扯了半小时 CountDownLatch 后,给我发 Offer?| 原力计划相关推荐

  1. 面试官:你连 RESTful 都不知道我怎么敢要你?| 原力计划

    作者 | zhanglinblog 责编 | 刘静 出品 | CSDN 博客 面试官:了解RESTful吗? 我:听说过. 面试官:那什么是RESTful? 我:就是用起来很规范,挺好的 面试官:是R ...

  2. 腾讯面试居然跟我扯了半小时的CountDownLatch

    文章持续更新,微信搜索「 万猫学社 」第一时间阅读. 关注后回复「 电子书 」,免费获取12本Java必读技术书籍. 一个长头发.穿着清爽的小姐姐,拿着一个崭新的Mac笔记本向我走来,看着来势汹汹,我 ...

  3. 头条面试居然跟我扯了半小时的Semaphore

    一个长头发.穿着清爽的小姐姐,拿着一个崭新的Mac笔记本向我走来,看着来势汹汹,我心想着肯定是技术大佬吧!但是我也是一个才华横溢的人,稳住我们能赢. 面试官:看你简历上有写熟悉并发编程,Semapho ...

  4. 我和面试官之间关于操作系统的一场对弈 | 原力计划

    作者 | Guide哥 责编 | 伍杏玲 出品 | CSDN博客 大家好,我是 Guide 哥!很多读者抱怨计算操作系统的知识点比较繁杂,自己也没有多少耐心去看,但是面试的时候又经常会遇到.所以,我带 ...

  5. sql三张表的搜索要满足5种搜索条件的模糊搜索_面试三轮我倒在了一道 SQL 题上……| 原力计划...

    作者 | 牧小农的夏天 责编 | 王晓曼 出品 | CSDN博客 前言 最近小农在找工作,因为今年疫情的特殊原因,导致工作不是特别好找,所以一旦有面试电话,如果可以,都会去试一试,刚好接到一个面试邀请 ...

  6. 不怕面试被问了!二叉树算法大盘点 | 原力计划

    作者 | BoCong-Deng 责编 | 伍杏玲 头图 | CSDN 下载自视觉中国 出品 | CSDN博客 树结构对于程序员来说应该不陌生,特别是二叉树,基本只要接触算法这一类的都一定会碰到的,所 ...

  7. Arrays.sort() 为什么可以对 int 等数组进行排序?我跟面试官扯了半个小时 | 原力计划...

    作者 | TrueDei 责编 | 王晓曼 出品 | CSDN博客 前言 排序是在程序开发中最常用到的,最常见的就是针对一些数字进行排序.而现实中像商品的名字,订单的日期等进行排序.Java的JDK中 ...

  8. 腾讯面试官这样问我二叉树,我刚好都会 | 原力计划

    作者 | 天才程序YUAN 责编 | 夕颜 封图 | CSDN下载自视觉中国 出品 | CSDN(ID:CSDNnews) 前记 上周我投递出了简历,岗位是后端开发工程师.这周腾讯面试官给我进行了视频 ...

  9. 面试官:背了几道面试题就敢说熟悉Java源码?我们不招连源码都不会看的人|原力计划...

    作者|Baldwin_KeepMind 责编|伍杏玲 出品|CSDN博客 我的真实经历 标题是我2019.6.28在深圳某500强公司面试时候面试官跟我说的话,即使是现在想起来,也是觉得无尽的羞愧,因 ...

最新文章

  1. winhttp get 实例_实例|CASS的绘制的航道断面,怎么多了两个边沟? 135
  2. es6 日期字符串转日期_量化数据预处理-中文日期(含)转英文日期
  3. redis 集群搭建_一文轻松搞懂redis集群原理及搭建与使用
  4. 用vim 配置javascript
  5. JVM调优-GC参数
  6. EasyUI numbox输入框,金额格式化显示
  7. 【报告分享】2020城市大脑规划建设与应用研究报告.pdf(附下载链接)
  8. 《剑指 Offer I》刷题笔记 41 ~ 50 题
  9. 程序设计竞赛中常用的STL汇总
  10. 利用Python绘制图案——七色花子
  11. 记一次Springboot 启动错误(三) xxx.jar中没有主清单属性
  12. 阿里云服务器如何登录?阿里云服务器的三种登录方法...
  13. SQL中drop,delete和truncate的异同
  14. ts视频合并---P站
  15. 复现 ASPCMS企业建站系统Cookies欺骗漏洞
  16. 感应封盖机行业调研报告 - 市场现状分析与发展前景预测(2021-2027年)
  17. Visual Studio 2019 发行说明
  18. 线性链表实现对二进制数加1运算
  19. 关于弹性布局flex
  20. 【系统架构】原型图的认识

热门文章

  1. java---同步与并发概念
  2. 词典 (noi OpenJudge)
  3. Static 静态内部类
  4. 2014百度之星资格赛——Disk Schedule
  5. 杭电 1021 找规律
  6. 如何将DataGrid中任意行背景色设置为#00A000?
  7. [论文翻译] Visual Saliency Transformer
  8. 【学习笔记】计算机导论之计算机硬件
  9. 分析设计数据库,优化Java程序
  10. oracle删除还原点,【赵强老师】删除表和Oracle的回收站