视频看了几百小时还迷糊?关注我,几分钟让你秒懂!
在高并发场景下,Java原生的 synchronized 和 volatile 虽然能解决部分问题,但面对复杂的并发控制(比如限流、倒计时、线程协作等),就显得力不从心了。这时,JUC(java.util.concurrent)包中的高并发工具类就成了我们的“神器”。
本文将结合 Spring Boot 项目实战,用通俗易懂的方式讲解几个核心 JUC 工具类,并附上正例、反例、注意事项,让小白也能快速上手!
🎯 需求场景
假设你正在开发一个电商秒杀系统:
- 用户点击“抢购”按钮后,多个请求同时涌入;
- 系统需要限制最多 100 个用户成功下单(限流);
- 所有请求需等待库存初始化完成才能开始处理(线程同步);
- 抢购结束后要汇总结果(线程协作)。
这些需求,用传统方式很难优雅实现,而 JUC 工具类可以轻松搞定!
一、CountDownLatch:倒计时门闩
✅ 作用
让一个或多个线程等待其他线程完成操作后再继续执行。
🔧 典型场景
- 主线程等待多个子任务完成后再汇总结果;
- 系统启动时等待多个服务初始化完毕。
💡 Spring Boot 正例代码
@RestController
public class CountDownLatchController {
@GetMapping("/init")
public String initSystem() throws InterruptedException {
int serviceCount = 3;
CountDownLatch latch = new CountDownLatch(serviceCount);
// 模拟3个服务初始化
for (int i = 1; i <= serviceCount; i++) {
final int serviceId = i;
new Thread(() -> {
try {
System.out.println("服务 " + serviceId + " 开始初始化...");
Thread.sleep(2000); // 模拟耗时
System.out.println("服务 " + serviceId + " 初始化完成!");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
latch.countDown(); // 计数减1
}
}).start();
}
latch.await(); // 主线程阻塞,直到计数归零
return "所有服务初始化完成,系统启动成功!";
}
}
❌ 反例(常见错误)
// 错误:没有在 finally 中调用 countDown()
new Thread(() -> {
if (someCondition) {
throw new RuntimeException("初始化失败");
}
latch.countDown(); // 如果异常抛出,这行不会执行!
}).start();
→ 后果:latch.await() 永远阻塞,程序卡死!
⚠️ 注意事项
countDown()必须放在finally块中,确保异常也能释放;await()可设置超时时间:latch.await(5, TimeUnit.SECONDS),避免永久阻塞。
二、Semaphore:信号量(限流神器)
✅ 作用
控制同时访问特定资源的线程数量,常用于限流。
🔧 典型场景
- 数据库连接池(最多10个连接);
- 秒杀系统限制并发用户数。
💡 Spring Boot 正例代码(模拟秒杀限流)
@Component
public class SeckillService {
// 最多允许10个用户同时抢购
private final Semaphore semaphore = new Semaphore(10);
public String seckill(Long userId) {
boolean acquired = false;
try {
// 尝试获取许可(非阻塞)
if (semaphore.tryAcquire(1, TimeUnit.SECONDS)) {
acquired = true;
// 模拟抢购逻辑
Thread.sleep(500);
return "用户 " + userId + " 抢购成功!";
} else {
return "用户 " + userId + " 抢购失败:系统繁忙,请稍后再试";
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return "用户 " + userId + " 抢购中断";
} finally {
if (acquired) {
semaphore.release(); // 一定要释放!
}
}
}
}
@RestController
public class SeckillController {
@Autowired
private SeckillService seckillService;
@GetMapping("/seckill")
public String seckill(@RequestParam Long userId) {
return seckillService.seckill(userId);
}
}
❌ 反例
// 错误:忘记 release()
if (semaphore.tryAcquire()) {
// 处理业务...
// 忘记 semaphore.release();
}
→ 后果:许可被永久占用,后续所有请求都被拒绝!
⚠️ 注意事项
release()必须在finally中调用;- 使用
tryAcquire(timeout)避免无限等待; - 信号量是公平/非公平可选的,高并发下通常用非公平(性能更好)。
三、CyclicBarrier:循环栅栏(线程协作)
✅ 作用
让一组线程互相等待,直到所有线程都到达某个屏障点,再一起继续执行。可重复使用(与 CountDownLatch 的关键区别)。
🔧 典型场景
- 多线程分片计算,每轮计算完成后汇总;
- 游戏房间:等所有玩家准备就绪才开始。
💡 Spring Boot 正例(多线程分片计算)
@Service
public class CyclicBarrierService {
public void runBatchTask() {
int threadCount = 4;
CyclicBarrier barrier = new CyclicBarrier(threadCount, () -> {
System.out.println("【汇总阶段】所有线程已完成本轮任务,开始汇总...");
});
for (int i = 0; i < threadCount; i++) {
final int taskId = i;
new Thread(() -> {
try {
// 第一轮任务
System.out.println("线程 " + taskId + " 执行任务 A");
Thread.sleep(1000);
barrier.await(); // 等待其他线程
// 第二轮任务
System.out.println("线程 " + taskId + " 执行任务 B");
Thread.sleep(1000);
barrier.await(); // 再次等待(可重用!)
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
}
❌ 反例
// 错误:线程数与 CyclicBarrier 构造参数不一致
CyclicBarrier barrier = new CyclicBarrier(3);
// 但只启动了2个线程 → 永远无法触发 barrier 动作!
⚠️ 注意事项
- 线程数量必须严格匹配
parties参数; - 若某个线程在
await()前崩溃,会导致其他线程永久阻塞(可用超时await(timeout)避免); - 支持在屏障动作中执行汇总逻辑(构造函数第二个参数)。
四、对比总结
| 工具类 | 是否可重用 | 主要用途 | 关键方法 |
|---|---|---|---|
| CountDownLatch | ❌ 否 | 一次性等待多个任务完成 | countDown(), await() |
| Semaphore | ✅ 是 | 控制并发访问数量(限流) | acquire(), release() |
| CyclicBarrier | ✅ 是 | 多线程阶段性同步 | await() |
✅ 最佳实践建议
- 优先使用 try-with-resources 或 finally 确保资源释放;
- 高并发下慎用阻塞操作,尽量使用带超时的方法;
- 不要滥用线程,结合线程池(如
@Async+ThreadPoolTaskExecutor)更安全; - 测试时模拟高并发:可用
jmeter或CompletableFuture并发调用验证。
结语
JUC 工具类是 Java 高并发编程的基石。掌握 CountDownLatch、Semaphore、CyclicBarrier,能让你在面对复杂并发场景时游刃有余。记住:工具虽好,用错反噬——务必注意资源释放和异常处理!
视频看了几百小时还迷糊?关注我,几分钟让你秒懂!
&spm=1001.2101.3001.5002&articleId=157211082&d=1&t=3&u=dac5f777088847e99532fd36324d2af4)
227

被折叠的 条评论
为什么被折叠?



