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 通过任务依赖关系控制顺序,而非线程间直接通信,更适合 异步非阻塞 场景(如网络请求编排)。

相关推荐
高松燈4 分钟前
kafka入门和核心概念介绍
后端
喵手6 分钟前
Java中Stream与集合框架的差异:如何通过Stream提升效率!
java·后端·java ee
喵手18 分钟前
你知道,如何使用Java的多线程机制优化高并发应用吗?
java·后端·java ee
青梅主码28 分钟前
坐标差 1 公分,返工一整天?试试这个转换窍门
后端
cxyxiaokui00129 分钟前
别让你的Java对象在内存里躺平!序列化带它看世界
后端·面试
白露与泡影39 分钟前
SpringBoot前后端token自动续期方案
spring boot·后端·状态模式
青梅主码1 小时前
重磅!《人工智能和大型语言模型的研究前景:应用、挑战和未来方向》:代理型 AI 和大语言模型是否可以整合?
后端
hui函数1 小时前
Flask-WTF表单验证全攻略
后端·python·flask·web·表单验证
喵手1 小时前
Java异常处理最佳实践:如何避免捕获到不必要的异常?
java·后端·java ee
猿java1 小时前
精通MySQL却不了解OLAP和 OLTP,正常吗?
java·后端·面试