1. 问题背景与核心挑战
多线程同步问题是并发编程中的经典难题,LeetCode 1115题"交替打印FooBar"提供了一个绝佳的学习场景。题目要求两个不同的线程分别调用foo()和bar()方法,保证输出序列严格遵循"foobarfoobar..."的交替模式,重复n次。这看似简单的需求背后,隐藏着几个关键挑战:
- 执行顺序的精确控制:必须确保foo和bar严格交替执行,不能出现连续两个foo或bar
- 线程间的通信机制:需要设计高效的线程间信号传递方式
- 避免竞态条件:防止因执行时序问题导致输出结果不符合预期
- 性能考量:同步机制应尽可能减少线程阻塞带来的性能损耗
在实际工程中,类似的场景广泛存在于生产者-消费者模型、任务流水线处理等场景。比如日志系统的异步写入、订单处理的状态流转等,都需要类似的线程协调机制。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 解决方案设计与选型分析
2.1 同步原语对比
实现线程同步有多种技术路线可选,我们需要根据题目特点选择最适合的方案:
| 同步机制 | 适用场景 | 本题适用性 | 实现复杂度 |
|---|---|---|---|
| synchronized | 简单的互斥访问 | 中等 | 低 |
| wait/notify | 条件等待的场景 | 高 | 中 |
| Lock/Condition | 复杂的条件等待 | 高 | 中 |
| Semaphore | 资源计数或准入控制 | 高 | 低 |
| CyclicBarrier | 多线程同步到达某个屏障点 | 低 | 高 |
经过比较,信号量(Semaphore)和条件变量(Condition)是最符合本题需求的方案。它们都能精确控制线程执行的先后顺序,且实现简洁高效。
2.2 信号量方案设计
信号量方案的核心思想是:
- 初始化两个信号量:fooSemaphore(初始许可=1)和barSemaphore(初始许可=0)
- foo()方法在输出前获取fooSemaphore,执行后释放barSemaphore
- bar()方法在输出前获取barSemaphore,执行后释放fooSemaphore
这种设计形成了完美的交替循环:
code复制foo线程:获取foo许可 → 打印foo → 释放bar许可
bar线程:获取bar
