当前位置:首页 > 后端开发 > 正文

Java两个线程怎么轮流执行?

在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();
    }
}

原理

Java两个线程怎么轮流执行?  第1张

  1. 共享锁 lock 作为同步监视器。
  2. 布尔标志 isThreadATurn 决定当前轮到哪个线程。
  3. wait() 释放锁并挂起线程,notifyAll() 唤醒等待线程。
  4. 严格交替: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();
    }
}

原理

  1. 初始化:A有1个许可(可执行),B有0个许可(阻塞)。
  2. A执行 → 释放B的许可 → B执行 → 释放A的许可 → 循环。

️ 关键注意事项

  1. 避免死锁
    • 确保每个wait()/await()都有对应的唤醒。
    • 锁释放:在finally块中释放显式锁(ReentrantLock)。
  2. 性能考量
    • 轻量级场景用synchronized,高并发场景用ReentrantLock
    • 信号量适用于多阶段复杂同步。
  3. 线程中断处理
    • 捕获InterruptedException并恢复中断状态(Thread.currentThread().interrupt())。
  4. 可见性保证
    • 共享变量(如isThreadATurn)需用volatile修饰(或通过锁保证可见性)。

总结建议

方案 适用场景 性能 复杂度
synchronized + 等待通知 简单交替逻辑 中等
ReentrantLock + Condition 高并发、精准控制
Semaphore 多许可控制(如超过2个线程轮流) 中等

推荐选择

  • 新手优先使用 synchronized 方案(简洁安全)。
  • 生产环境推荐 ReentrantLock + Condition(性能最优)。

引用说明

  • Java并发模型基于《Java并发编程实战》(Brian Goetz等)。
  • ReentrantLock 设计参考Oracle官方文档Java 17 Lock API。
  • 线程同步原则遵循JLS(Java语言规范)第17章内存模型。
0