上一篇
Java两个线程怎么轮流执行?
- 后端开发
- 2025-06-08
- 3166
在Java中轮流执行两个线程可通过synchronized配合wait/notify机制实现:共享同一监视器锁,线程执行后主动等待并唤醒对方,严格交替运行,也可用ReentrantLock与双Condition精确控制切换,确保线程间顺序协作。
在Java中实现两个线程轮流执行(交替运行)的核心在于线程间通信与同步,以下是几种专业、高效且符合生产环境标准的实现方案,每种方案附带完整代码示例和原理解析:
使用 synchronized
+ wait()
/notify()
(经典方案)
通过共享对象的监视器锁控制线程执行顺序:
public class TurnTakingWithWaitNotify { private static final Object lock = new Object(); private static boolean isThreadATurn = true; // 控制标志 public static void main(String[] args) { Runnable taskA = () -> { try { synchronized (lock) { for (int i = 0; i < 5; i++) { while (!isThreadATurn) lock.wait(); // 非A的回合则等待 System.out.println("Thread-A: " + i); isThreadATurn = false; lock.notifyAll(); // 唤醒B } } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }; Runnable taskB = () -> { try { synchronized (lock) { for (int i = 0; i < 5; i++) { while (isThreadATurn) lock.wait(); // 非B的回合则等待 System.out.println("Thread-B: " + i); isThreadATurn = true; lock.notifyAll(); // 唤醒A } } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }; new Thread(taskA).start(); new Thread(taskB).start(); } }
原理:
- 共享锁
lock
作为同步监视器。 - 布尔标志
isThreadATurn
决定当前轮到哪个线程。 wait()
释放锁并挂起线程,notifyAll()
唤醒等待线程。- 严格交替:A执行 → 唤醒B → B执行 → 唤醒A → 循环。
使用 ReentrantLock
+ Condition
(高性能方案)
利用显式锁的精准唤醒机制,避免无效竞争:
import java.util.concurrent.locks.*; public class TurnTakingWithReentrantLock { private static final ReentrantLock lock = new ReentrantLock(); private static final Condition conditionA = lock.newCondition(); private static final Condition conditionB = lock.newCondition(); private static boolean isThreadATurn = true; public static void main(String[] args) { Runnable taskA = () -> { lock.lock(); try { for (int i = 0; i < 5; i++) { while (!isThreadATurn) conditionA.await(); System.out.println("Thread-A: " + i); isThreadATurn = false; conditionB.signal(); // 精准唤醒B } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { lock.unlock(); } }; Runnable taskB = () -> { lock.lock(); try { for (int i = 0; i < 5; i++) { while (isThreadATurn) conditionB.await(); System.out.println("Thread-B: " + i); isThreadATurn = true; conditionA.signal(); // 精准唤醒A } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { lock.unlock(); } }; new Thread(taskA).start(); new Thread(taskB).start(); } }
优势:
- 精确控制唤醒目标(
signal()
替代notifyAll()
),减少无效线程竞争。 - 更高的并发性能(JDK推荐替代
synchronized
的方案)。
使用 Semaphore
(信号量方案)
通过两个信号量控制执行权限:
import java.util.concurrent.Semaphore; public class TurnTakingWithSemaphore { private static final Semaphore semaphoreA = new Semaphore(1); // A先执行 private static final Semaphore semaphoreB = new Semaphore(0); public static void main(String[] args) { Runnable taskA = () -> { try { for (int i = 0; i < 5; i++) { semaphoreA.acquire(); // 获取A的许可 System.out.println("Thread-A: " + i); semaphoreB.release(); // 释放B的许可 } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }; Runnable taskB = () -> { try { for (int i = 0; i < 5; i++) { semaphoreB.acquire(); // 获取B的许可 System.out.println("Thread-B: " + i); semaphoreA.release(); // 释放A的许可 } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }; new Thread(taskA).start(); new Thread(taskB).start(); } }
原理:
- 初始化:A有1个许可(可执行),B有0个许可(阻塞)。
- A执行 → 释放B的许可 → B执行 → 释放A的许可 → 循环。
️ 关键注意事项
- 避免死锁:
- 确保每个
wait()
/await()
都有对应的唤醒。 - 锁释放:在
finally
块中释放显式锁(ReentrantLock
)。
- 确保每个
- 性能考量:
- 轻量级场景用
synchronized
,高并发场景用ReentrantLock
。 - 信号量适用于多阶段复杂同步。
- 轻量级场景用
- 线程中断处理:
- 捕获
InterruptedException
并恢复中断状态(Thread.currentThread().interrupt()
)。
- 捕获
- 可见性保证:
- 共享变量(如
isThreadATurn
)需用volatile
修饰(或通过锁保证可见性)。
- 共享变量(如
总结建议
方案 | 适用场景 | 性能 | 复杂度 |
---|---|---|---|
synchronized + 等待通知 |
简单交替逻辑 | 中等 | |
ReentrantLock + Condition |
高并发、精准控制 | 高 | |
Semaphore |
多许可控制(如超过2个线程轮流) | 中等 |
推荐选择:
- 新手优先使用
synchronized
方案(简洁安全)。- 生产环境推荐
ReentrantLock
+Condition
(性能最优)。
引用说明:
- Java并发模型基于《Java并发编程实战》(Brian Goetz等)。
ReentrantLock
设计参考Oracle官方文档Java 17 Lock API。- 线程同步原则遵循JLS(Java语言规范)第17章内存模型。