synchronized仅通过锁的串行化和happens-before保证临界区互斥与内存可见性,不直接控制逻辑顺序;需统一锁对象、配合状态变量或队列等显式逻辑实现fileA→fileB→fileC的严格顺序。

synchronized 本身不直接保证“代码逻辑执行顺序”,它只确保临界区的互斥性和线程间操作的可见性与有序性边界;真正让多线程按预期顺序执行,依赖的是锁的串行化效果 + 显式控制逻辑。
锁的串行化效果决定宏观执行次序
当多个线程竞争同一把锁(如 static final Object LOCK = new Object();),它们必须排队进入 synchronized 块。谁先抢到锁,谁就先执行——这形成了天然的串行执行流。虽然调度顺序不可控,但所有写操作在锁保护下必然一个接一个发生,不会交错。
- 用不同锁对象(如
synchronized(file1)和synchronized(file2))无法形成全局顺序,因为彼此不感知 - 必须统一使用同一个锁对象,才能让跨文件、跨方法的操作被强制串行化
- 锁释放后,下一个获取锁的线程看到的是前一个线程写入的最新状态(happens-before 保证)
临界区内需主动控制业务顺序
获得锁只是第一步,真正按 fileA → fileB → fileC 的顺序写入,得靠程序逻辑判断,而不是靠线程抢锁运气。
- 用状态变量控制:比如
private static int nextToWrite = 0;,进入同步块后检查if (nextToWrite == 0)才写 fileA,写完再nextToWrite++ - 用任务队列驱动:把要写的文件封装成任务,放进
BlockingQueue,同步块里只取一个任务执行,自然按入队顺序处理 - 避免在 synchronized 块里做实际 IO:可先在块内决定“该写哪个”并准备参数,释放锁后再执行耗时写入,但要注意数据传递安全(如用不可变对象或深拷贝)
有序性保障来自 happens-before 规则
synchronized 不禁止临界区内部指令重排(只要单线程语义不变),但它通过锁的获取与释放建立明确的内存顺序约束:
立即学习“Java免费学习笔记(深入)”;
- 线程 A 释放锁前的所有写操作,对线程 B 获取同一锁后的读操作可见
- 这使得“写 fileA 完成”这个动作,在逻辑上一定先于“读 fileA 内容用于后续判断”
- 这种顺序不是编译器或 CPU 强制的指令序,而是线程间观察结果的一致性保证
更可控的替代方案
如果业务要求严格等待(比如必须等 fileA 写完通知 fileB 开始),synchronized 配合 wait()/notify() 容易出错(虚假唤醒、条件检查遗漏)。这时更适合:
-
ReentrantLock+Condition:支持多个等待队列,条件唤醒更精准 -
CountDownLatch或CyclicBarrier:适用于固定阶段协同 -
CompletableFuture链式调用:适合异步顺序编排,不依赖共享锁


















