
synchronized 仅保证对共享资源的互斥访问,不控制线程调度顺序;A、B、C 的立即输出源于主线程执行速度快于子线程启动和首次打印,而线程实际执行时序由操作系统调度器决定,具有不确定性。
`synchronized` 仅保证对共享资源的互斥访问,不控制线程调度顺序;a、b、c 的立即输出源于主线程执行速度快于子线程启动和首次打印,而线程实际执行时序由操作系统调度器决定,具有不确定性。
在 Java 多线程编程中,一个常见误区是认为 synchronized 关键字能“串行化线程执行顺序”——即让多个线程严格按照代码书写顺序(如 t1 → 打印 A → t2 → 打印 B)依次运行。但事实并非如此:synchronized 解决的是“竞态条件”(race condition),而非“执行时序控制”(scheduling order)。
上述示例中,AB.print() 方法被声明为 synchronized,意味着:当多个线程调用同一 AB 实例的 print() 时,它们会排队进入该方法,确保任意时刻只有一个线程在执行其临界区(即 for 循环体)。这确实防止了输出混杂(如 "1 0", "2 0", "1 1" 交错),但绝不保证线程何时开始执行、何时获得 CPU 时间片。
关键点在于:
-
new Thread(...).start()是异步操作:它仅向 JVM 和 OS 提交调度请求,不阻塞主线程; - 主线程在
t1.t.start()后立即执行System.out.println("A"),而此时t1可能尚未真正进入run(),更未到达obj.print(name)的第一行println; -
Thread.sleep(1000)在print()内部进一步放大了延迟,使得主线程早已完成 A/B/C 输出,并启动t2/t3,甚至进入join()等待阶段; - 操作系统线程调度器基于优先级、时间片、内核状态等动态决策,每次运行结果都可能不同——这也是为何多次运行会出现
A,t1,B,C,t2,t3、A,B,C,t1,t2,t3或偶发的“符合预期”顺序。
✅ 正确理解 synchronized 的作用范围:
class AB {
// ✅ 正确:锁住当前实例(this),保护对共享状态的并发修改
synchronized void print(String name) { ... }
// ❌ 错误假设:以下写法不能强制 t1 先于 t2 执行
// synchronized 不等于 "t1.run() 必须在 t2.run() 开始前完成"
}
⚠️ 若需严格控制执行顺序(例如:必须先完成 t1 全部输出,再执行 println("A")),应使用显式同步机制,例如:
-
CountDownLatch(推荐):CountDownLatch latch = new CountDownLatch(1); // 在 t1.run() 开头:latch.await(); 在主线程 A 前:latch.countDown();
-
join()(但注意:join()是等待线程终止,而非等待其“开始执行”); -
ReentrantLock配合Condition实现更精细的唤醒逻辑。
? 总结:synchronized 是互斥锁(mutual exclusion),不是执行栅栏(execution barrier)。它保障“同一时刻最多一个线程执行某段代码”,但不承诺“哪个线程先抢到锁”或“主线程与子线程的相对启动时机”。真正的线程协调必须借助 wait/notify、CountDownLatch、CyclicBarrier 或 CompletableFuture 等高级并发工具。盲目依赖 synchronized 实现顺序逻辑,是并发 bug 的高发源头。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











