4种Java实现线程同步顺序打印

方法1:使用 synchronized + wait/notify

通过 共享锁 和 条件等待 实现线程顺序控制。

csharp 复制代码
public class SyncWaitNotifyDemo {
    private static final Object lock = new Object();
    
    private static int state = 1; 

    public static void main(String[] args) {
        new Thread(() -> print(1, 2, "A")).start();
        new Thread(() -> print(2, 3, "B")).start();
        new Thread(() -> print(3, 1, "C")).start();
    }

    private static void print(int targetState, int nextState, String name) {
        for (int i = 0; i < 3; i++) { // 打印3轮
            synchronized (lock) {
                while (state != targetState) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.print(name + ":" + targetState + " ");
                state = nextState;
                lock.notifyAll(); // 唤醒所有等待线程
            }
        }
    }
}

原理:

  • 每个线程通过检查 state 是否匹配自己的目标状态来决定是否执行。
  • 若条件不满足,线程调用 wait() 释放锁并进入等待。
  • 当前线程执行完毕后更新 state,并通过 notifyAll() 唤醒其他线程。

方法2:使用 ReentrantLock + Condition

利用 显式锁 和 条件变量 实现精准唤醒。

java 复制代码
import java.util.concurrent.locks.*;

public class ReentrantLockConditionDemo {
    private static final ReentrantLock lock = new ReentrantLock();
    private static final Condition conditionA = lock.newCondition();
    private static final Condition conditionB = lock.newCondition();
    private static final Condition conditionC = lock.newCondition();
    private static int state = 1;

    public static void main(String[] args) {
        new Thread(() -> print(1, 2, conditionA, conditionB, "A")).start();
        new Thread(() -> print(2, 3, conditionB, conditionC, "B")).start();
        new Thread(() -> print(3, 1, conditionC, conditionA, "C")).start();
    }

    private static void print(int targetState, int nextState, 
                              Condition current, Condition next, String name) {
        for (int i = 0; i < 3; i++) {
            lock.lock();
            try {
                while (state != targetState) {
                    current.await();
                }
                System.out.print(name + ":" + targetState + " ");
                state = nextState;
                next.signal(); // 精准唤醒下一个线程
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }
}

原理:

  • 每个线程绑定一个独立的 Condition,通过 await() 和 signal() 精准控制唤醒目标。
  • 避免无效的线程竞争,提高效率。

方法3:使用 Semaphore(信号量)

通过 信号量许可 控制线程执行顺序。

java 复制代码
import java.util.concurrent.Semaphore;

public class SemaphoreDemo {
    private static Semaphore semaphoreA = new Semaphore(1);
    private static Semaphore semaphoreB = new Semaphore(0);
    private static Semaphore semaphoreC = new Semaphore(0);

    public static void main(String[] args) {
        new Thread(() -> print(1, semaphoreA, semaphoreB, "A")).start();
        new Thread(() -> print(2, semaphoreB, semaphoreC, "B")).start();
        new Thread(() -> print(3, semaphoreC, semaphoreA, "C")).start();
    }

    private static void print(int num, Semaphore current, Semaphore next, String name) {
        for (int i = 0; i < 3; i++) {
            try {
                current.acquire(); // 获取当前信号量许可
                System.out.print(name + ":" + num + " ");
                next.release();    // 释放下一个信号量许可
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

原理:

  • 初始时,只有第一个线程(A)拥有许可(semaphoreA=1)。
  • 每个线程执行后释放下一个线程的许可,形成链式触发。

方法4:使用 CompletableFuture 链式调用

通过 异步任务链 隐式控制执行顺序,无需显式同步。

arduino 复制代码
import java.util.concurrent.CompletableFuture;

public class CompletableFutureDemo {
    public static void main(String[] args) {
        CompletableFuture<?> future = CompletableFuture.completedFuture(null);
        for (int i = 0; i < 3; i++) { // 打印3轮
            future = future.thenRunAsync(() -> System.out.print("A:1 "))
                           .thenRunAsync(() -> System.out.print("B:2 "))
                           .thenRunAsync(() -> System.out.print("C:3 "));
        }
        future.join(); // 阻塞等待所有任务完成
    }
}

原理:

  • 使用 CompletableFuture 的链式方法 thenRunAsync,确保每个任务在前一个任务完成后触发。
  • 默认使用 ForkJoinPool 线程池,每个 thenRunAsync 可能由不同线程执行,但顺序严格保证。
  • 通过循环构建任务链,实现多轮顺序打印。

对比与总结

方法 优点 缺点
synchronized 实现简单,无需额外依赖 无法精准唤醒,可能产生竞争
ReentrantLock 条件变量精准控制,灵活性高 代码复杂度稍高
Semaphore 逻辑清晰,适合固定顺序场景 需要预先分配许可,扩展性有限
CompletableFuture 代码简洁,天然支持异步编程 依赖线程池,无显式线程控制

适用场景:

  • 简单场景 :优先选择 synchronized。
  • 精准控制 :使用 ReentrantLock + Condition。
  • 链式触发 :Semaphore 或 CompletableFuture。
  • 异步响应式 :CompletableFuture 更符合现代编程范式

扩展思考:CompletableFuture 的底层逻辑

  1. 任务链与线程池
    thenRunAsync 的每个阶段默认由 ForkJoinPool 中的线程执行,但可以通过自定义线程池指定执行线程。例如:

    ini 复制代码
    ExecutorService executor = Executors.newFixedThreadPool(3);
    future.thenRunAsync(() -> System.out.print("A:1 "), executor);
  2. 与同步锁的区别
    CompletableFuture 通过任务依赖关系控制顺序,而非线程间直接通信,更适合 异步非阻塞 场景(如网络请求编排)。

相关推荐
深蓝AI6 小时前
Python 3.13 的 Queue.shutdown:异步消费者终于能正常收工了
后端
武子康7 小时前
MultiNet6 空闲崩溃:`EXCVADDR=0x8` 后又出现 `CORRUPT HEAP`
人工智能·后端·agent
武子康8 小时前
MultiNet7 每轮都 clean,ESP32-P4 内存为什么还在掉?
人工智能·后端·agent
超级架构师8 小时前
PRAXO-CORE 2.5.0:用 conformance 和显式边界构建 Go Agent 框架
开发语言·人工智能·后端·golang·ai编程·智能体框架·praxo-core
陈随易8 小时前
AI 运维服务器,安全又可靠的方法
前端·后端·程序员
IT_陈寒10 小时前
JavaScript的this坑人实录,这次我真的记下来了
前端·人工智能·后端
starzy199011 小时前
Scala类和对象:从构造器到样例类的完整指南
开发语言·后端·scala
梦影_11 小时前
MySQL进阶教程(五)—— SQL调优(下)
android·大数据·数据库·后端·sql·mysql·database
考虑考虑11 小时前
mariadb数据库
数据库·后端·mariadb
明月_清风11 小时前
用自然语言操控真机:Google Artemis 移动端 AI Agent 上手与实践
人工智能·后端