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
  • 链式触发SemaphoreCompletableFuture
  • 异步响应式CompletableFuture 更符合现代编程范式

扩展思考:CompletableFuture 的底层逻辑

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

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

相关推荐
IT_陈寒20 分钟前
Redis 性能提升30%的7个关键优化策略,90%开发者都忽略了第3点!
前端·人工智能·后端
Victor3561 小时前
Redis(137)Redis的模块机制是什么?
后端
Victor3561 小时前
Redis(136)Redis的客户端缓存是如何实现的?
后端
不知更鸟6 小时前
Django 项目设置流程
后端·python·django
黄昏恋慕黎明7 小时前
spring MVC了解
java·后端·spring·mvc
G探险者9 小时前
为什么 VARCHAR(1000) 存不了 1000 个汉字? —— 详解主流数据库“字段长度”的底层差异
数据库·后端·mysql
百锦再9 小时前
第18章 高级特征
android·java·开发语言·后端·python·rust·django
Tony Bai9 小时前
Go 在 Web3 的统治力:2025 年架构与生态综述
开发语言·后端·架构·golang·web3
程序猿202310 小时前
项目结构深度解析:理解Spring Boot项目的标准布局和约定
java·spring boot·后端
RainbowSea10 小时前
内网穿透配置和使用
java·后端