首页 >文档 > countdownlatch和semaphore

countdownlatch和semaphore

深入解析Java并发工具:CountDownLatch与Semaphore核心区别与应用场景。掌握CountDownLatch的"一次性门闩"机制,实现多线程任务协同;了解Semaphore的"资源许可证"模式,有效控制并发访问量。本文通过王者荣耀开黑、充电桩管理等生动案例,详解两者在初始化协调、连接池管理、接口限流等场景的应用。面试必备:对比表格清晰呈现核心差异,常见追问深度剖析,助你轻松应对Java并发面试难题。附2025年最新Java面试宝典资源,提升技术实力,斩获高薪offer!

CountDownLatch和Semaphore:并发工具面试题深度拆解

大家好,今天咱们来聊聊面试中高频出现的并发工具:CountDownLatchSemaphore。这两个家伙都属于java.util.concurrent包里的“利器”,名字听着唬人,但理解了机制其实很清晰。面试官最爱问它们的区别和适用场景,搞明白了,能让你在并发题上轻松过关!

面试鸭返利网

2025年Java面试宝典重磅分享:

链接: https://pan.baidu.com/s/1RUVf75gmDVsg8MQp4yRChg?pwd=9b3g 提取码: 9b3g


CountDownLatch:一次性的“倒计时门闩”

想象一个场景:你组织了一场王者荣耀开黑,需要等5个队友都确认“准备好了”,才能一起点“开始匹配”。CountDownLatch 干的就是这个协调的活儿!

  • 核心机制: 初始化时设定一个计数器(比如new CountDownLatch(5))。每当一个任务完成,就调用countDown(),计数器减1。调用await()的线程(比如主线程)会阻塞,直到计数器减到0才放行。
  • 关键特点:
    • 一次性: 计数器归零后,再调用await()会立刻放行,无法重置复用。想再用?new一个新的。
    • 主等子: 通常是主线程(或协调者)在await(),等待一组工作线程完成任务(调用countDown())。
    • 不可增加: 计数器只能减,不能加。
  • 典型面试场景:
    • “启动服务时,如何确保所有依赖的组件都初始化完成再对外提供服务?”
    • “多线程计算一个大任务,如何等所有子任务都算完再汇总结果?”
    • “模拟并发压测,怎么让所有用户线程在同一时刻开始发起请求?”(主线程调用countDownLatch.countDown()作为发令枪)
// 伪代码口述思路:
// 1. main线程创建CountDownLatch(5)
// 2. 启动5个工作线程,每个线程干完活就latch.countDown()
// 3. main线程调用latch.await(),等5个都完成
// 4. 5个都countDown后,main线程解除阻塞,继续执行汇总逻辑

Semaphore:灵活的“资源许可证管理器”

再想象一个场景:小区里只有3个充电桩,却有10辆电动车要充电。Semaphore 就是管理这3个充电桩的“物业”。

  • 核心机制: 初始化时指定许可证(permits)数量(比如new Semaphore(3))。线程想访问共享资源,必须先调用acquire()申请一个许可证(如果没有空闲的,线程就阻塞等待)。用完后,必须调用release()归还许可证,以便其他线程使用。
  • 关键特点:
    • 可复用: 许可证可以循环使用(acquire -> release -> acquire -> release ...)。
    • 控制并发量: 核心功能!限制同时访问某一资源的线程数。比如数据库连接池、限流。
    • 可增减: 初始化后,可以通过release()增加许可(虽然不常用),也可以通过构造方法或reducePermits减少(需小心)。
    • 公平/非公平: 构造方法可以选择是否是公平锁(影响线程获取许可证的顺序)。
  • 典型面试场景:
    • “如何实现一个线程安全的、固定大小的数据库连接池?”
    • “系统接口要限流,每秒最多处理100个请求,怎么实现?”
    • “有10个打印任务,但只有2台打印机,如何控制?”
    • “对比Semaphore和线程池,它们都能限制并发数,区别在哪?”(线程池管理的是工作线程本身的生命周期和任务队列;Semaphore只控制并发访问资源的入口数量,不管理线程。)
// 伪代码口述思路(连接池):
// 1. 初始化Semaphore(10) // 假设10个连接
// 2. 线程想获取连接:先semaphore.acquire() // 申请许可证,没许可证就等
// 3. 拿到许可证后,从池里取一个真实连接用
// 4. 用完连接,放回池里
// 5. 调用semaphore.release() // 归还许可证,让其他线程能用

面试鸭返利网


核心差异一目了然

| 特性 | CountDownLatch | Semaphore | | :----------- | :-------------------------- | :---------------------------- | | 核心目的 | 等待事件完成 | 控制并发访问资源量 | | 计数器 | 减到0触发 | 许可证数量 (>=0) | | 重置性 | 不可重置 (一次性) | 可复用 (acquire/release) | | 修改方向 | 只能减 (countDown) | 可增 (release) 可减 (acquire) | | 主要用法 | 主线程 await() 等子线程 | 工作线程自己 acquire/release | | 典型场景 | 初始化协调、多任务结果汇总 | 连接池、资源池、限流 |

简单记:CountDownLatch是“等大家干完活开会”;Semaphore是“名额有限,先到先得,用完归还”。


面试官可能会怎么追问?

  1. CountDownLatch 能代替 join() 吗?为什么更好?

    • 能。比 join() 更灵活,join()必须等线程结束,CountDownLatch只要任务完成就能countDown(),线程不一定结束。而且可以等待多个不同的线程组。
  2. Semaphoreacquire()release() 数量必须严格匹配吗?

    • 强烈建议匹配! 不匹配会导致:
      • acquire() 多了:许可证变负数,后续线程阻塞,可能死锁。
      • release() 多了:许可证总数变多,可能超过你期望的限制,导致过度并发。切记在finally块中释放!
  3. 怎么用 Semaphore 实现互斥锁 (Mutex)?

    • 初始化 Semaphore(1)acquire()相当于lock()release()相当于unlock()。这就是一个非公平的互斥锁。
  4. CyclicBarrierCountDownLatch 有啥区别?

    • CountDownLatch:主等子,一次性,计数器单向减。
    • CyclicBarrier:子等子(所有线程互相等),可循环使用(reset()),计数器内部重置,到达屏障点可以执行一个可选任务(Runnable)。

面试技巧:如何清晰表达

被问到区别时,不要死背概念。结合具体例子:

CountDownLatch 就像火箭发射前的检查,要等所有系统(引擎、导航、通信…)都回报‘Ready’(countDown()),总控台(await())才点火发射。是一次性的协作。

Semaphore 更像停车场入口的闸机,总共50个车位(permits=50)。车来了要拿卡(acquire())才能进,没卡等;车走了要还卡(release())让别的车进。它控制的是同时停车的数量,闸机(Semaphore)可以一直用。”

面试鸭返利网

用好并发工具,写出高效稳定的代码是面试加分项! 如果你正在准备面试,需要系统刷题和深入理解Java并发,可以考虑购买面试鸭会员获取海量真题解析和深度资料。对了,通过 面试鸭返利网 (mianshiyafanli.com) 找我购买,还能享受 25元返利 哦!

理解清楚 CountDownLatchSemaphore 的设计思想和适用场景,下次遇到并发协调问题,你就能快速选出最合适的工具了!加油!

如果你想获取更多关于面试鸭的优惠信息,可以访问面试鸭返利网面试鸭优惠网,了解最新的优惠活动和返利政策。

🎯 立即加入面试鸭会员 →

今日有支付宝大红包赶快领,手慢无

支付宝红包二维码

支付宝扫码领取1-8元无门槛红包

支付宝红包二维码