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

相关推荐
Java中文社群6 分钟前
面试官:你项目是如何保证高可用的?
java·后端·面试
冲鸭ONE1 小时前
for循环优化方式有哪些?
后端·性能优化
兮动人1 小时前
DBeaver连接OceanBase数据库
后端
刘鹏3781 小时前
深入浅出Java中的CAS:原理、源码与实战应用
后端
Lx3521 小时前
《从头开始学java,一天一个知识点》之:循环结构:for与while循环的使用场景
java·后端
fliter1 小时前
RKE1、K3S、RKE2 三大 Kubernetes 发行版的比较
后端
aloha_1 小时前
mysql 某个客户端主机在短时间内发起了大量失败的连接请求时
后端
程序员爱钓鱼1 小时前
Go 语言高效连接 SQL Server(MSSQL)数据库实战指南
后端·go·sql server
xjz18421 小时前
Java AQS(AbstractQueuedSynchronizer)实现原理详解
后端
Victor3561 小时前
Zookeeper(97)如何在Zookeeper中实现分布式协调?
后端