并发编程"黑科技":Semaphore和CountDownLatch的5个实战技巧,学会秒杀面试!

并发编程"黑科技":Semaphore和CountDownLatch的5个实战技巧,学会秒杀面试!

一、面试官最爱问的并发工具类,你真的会用吗?

大家好,我是老码农阿强!今天咱们来聊点硬核又实用的——Java并发包里的"瑞士军刀":Semaphore和CountDownLatch。这些工具就像厨房里的专用刀具,用对了事半功倍,用错了可能还不如一把菜刀来得实在。

上周面试一个5年经验的程序员,问他Semaphore怎么用,他居然回答"用来控制线程数量的"——这话没错,但只说对了10%!今天我就带大家深挖这些并发工具的隐藏用法,看完让你在项目中少走三年弯路。

二、Semaphore:不止是"线程计数器"

1. 停车场思维理解信号量
Semaphore就像停车场的闸机,permits参数就是车位数量。线程获取许可(acquire())相当于开车进停车场,释放许可(release())就像开车离开。

// 创建5个车位的停车场
Semaphore semaphore = new Semaphore(5);

// 开车进停车场(获取许可)
semaphore.acquire();
try {
    // 执行业务逻辑
} finally {
    // 开车离开(释放许可)
    semaphore.release();
}

2. 实战技巧:动态调整系统并发度
秒杀系统中可以用Semaphore控制同时处理的订单数量:

// 根据服务器CPU核心数动态调整许可数量
int permits = Runtime.getRuntime().availableProcessors() * 2;
Semaphore orderSemaphore = new Semaphore(permits);

// 秒杀接口中
orderSemaphore.acquire();
try {
    // 处理订单逻辑
} finally {
    orderSemaphore.release();
}

3. 高级操作:tryAcquire实现非阻塞获取

// 尝试获取许可,500毫秒超时
if (semaphore.tryAcquire(500, TimeUnit.MILLISECONDS)) {
    try {
        // 成功获取许可
    } finally {
        semaphore.release();
    }
} else {
    // 获取失败,返回友好提示
}

三、CountDownLatch:线程协作的"倒计时器"

1. 运动会开幕式理解闭锁
想象运动会开幕式需要等待所有方阵入场才能开始。每个方阵到达后倒计时减1,当倒计时归零时,主持人宣布开幕。

// 创建倒计时器,初始计数为10(10个方阵)
CountDownLatch latch = new CountDownLatch(10);

// 每个方阵线程
new Thread(() -> {
    try {
        // 方阵入场
    } finally {
        // 计数减1
        latch.countDown();
    }
}).start();

// 等待所有方阵入场(阻塞直到计数为0)
latch.await();
// 宣布开幕

2. 实战技巧:批量接口的超时控制

// 最多等待3秒,避免无限阻塞
if (latch.await(3, TimeUnit.SECONDS)) {
    // 所有任务完成
} else {
    // 部分任务超时,做降级处理
}

3. 高级用法:实现线程池的优雅关闭

// 等待所有任务完成再关闭线程池
CountDownLatch latch = new CountDownLatch(taskCount);

executorService.submit(() -> {
    try {
        // 任务逻辑
    } finally {
        latch.countDown();
    }
});

latch.await();
executorService.shutdown();

四、90%的人都会踩的3个坑

  1. Semaphore忘记释放许可

    // 错误示范
    if (condition) {
        semaphore.acquire();
        // 业务逻辑
        semaphore.release(); // 若业务逻辑抛异常,这里不会执行
    }
    

    ✅ 正确做法:必须在finally中释放

  2. CountDownLatch计数设置错误

    // 错误示范:创建时计数为5,却调用了6次countDown()
    CountDownLatch latch = new CountDownLatch(5);
    for (int i = 0; i < 6; i++) {
        new Thread(latch::countDown).start();
    }
    

    ✅ 正确做法:确保countDown()调用次数与初始计数一致

  3. 滥用CountDownLatch代替线程池
    不要为每个任务创建新线程,应该配合线程池使用

五、并发工具类的选型指南

工具类典型场景核心优势注意事项
Semaphore限流、资源池可动态调整许可数必须释放许可
CountDownLatch等待多任务完成简单直观计数不可重置
CyclicBarrier多线程协同工作可重复使用parties必须一致
Phaser复杂阶段任务动态调整参与方实现复杂

六、写在最后

并发工具类就像乐高积木,单独使用功能有限,组合起来却能搭建复杂系统。比如:

  • Semaphore + CountDownLatch:实现有限线程池的批量任务执行
  • CyclicBarrier + Phaser:处理分阶段的大数据计算

最后留一道思考题:如何用Semaphore实现一个简单的限流器?欢迎在评论区留下你的实现方案,最佳答案将获得《Java并发编程实战》电子书!


推荐阅读:

  • 《Java并发编程的艺术》
  • 阿里技术团队:《Java并发容器最佳实践》
  • 美团技术博客:《高并发场景下的限流策略》

标题:并发编程"黑科技":Semaphore和CountDownLatch的5个实战技巧,学会秒杀面试!
作者:jiangyi
地址:http://www.jiangyi.space/articles/2025/12/21/1766304300846.html

    0 评论
avatar