
synchronized 仅保证对同一对象的临界区互斥访问,不控制线程启动时机、调度顺序或主线程与子线程间的执行先后;A/B/C 的快速输出发生在子线程真正开始执行 print() 之前,这是由 JVM 线程调度和语句执行时序决定的。
synchronized 仅保证对同一对象的临界区互斥访问,不控制线程启动时机、调度顺序或主线程与子线程间的执行先后;a/b/c 的快速输出发生在子线程真正开始执行 `print()` 之前,这是由 jvm 线程调度和语句执行时序决定的。
在 Java 多线程编程中,一个常见误区是认为 synchronized 关键字能“串行化整个线程生命周期”——包括线程创建、启动、执行起始点以及与其他线程(如主线程)的相对执行顺序。但事实并非如此:synchronized 只作用于被它保护的代码块或方法,确保同一时刻最多一个线程能进入该临界区;它既不阻塞线程启动,也不影响线程调度优先级,更不提供跨线程的执行顺序保证。
以您提供的代码为例,关键执行流程如下:
-
AB t = new AB();创建共享同步对象; -
NewThread t1 = new NewThread("1", t);→ 内部调用t.start()启动线程,但start()仅向 JVM/OS 发出调度请求,并不立即执行run(); - 主线程紧接着执行
System.out.println("A");—— 这是一条轻量、无阻塞的语句,几乎瞬时完成; - 同理,
t2、t3的创建与"B"、"C"打印快速连续发生; - 此时,三个子线程可能尚未被 OS 调度到 CPU,或刚进入
run(),甚至还没开始执行obj.print(name)中的循环——而主线程早已打印完 A/B/C 并继续向下运行。
⚠️ 注意:synchronized public void print(...) 确保了当多个线程调用 t.print(...) 时,它们会排队执行(每次只允许一个进入),但这完全不约束 t1.t.start() 和 System.out.println("A") 谁先“到达”CPU。二者属于不同线程(主线程 vs 子线程),且无任何 happens-before 关系或显式同步机制(如 CountDownLatch、join() 前置等待等),因此其相对执行顺序是非确定性的(non-deterministic),取决于操作系统线程调度器、JVM 实现、CPU 负载等运行时因素。
下面是一个增强可观察性的对比示例:
立即学习“Java免费学习笔记(深入)”;
// 修改 NewThread 构造函数,添加启动确认日志
NewThread(String name, AB o) {
this.name = name;
obj = o;
t = new Thread(this, name);
System.out.println("[" + name + "] Thread object created, about to start...");
t.start();
System.out.println("[" + name + "] Thread started (start() returned).");
}你会发现输出类似:
[1] Thread object created, about to start... [1] Thread started (start() returned). A [2] Thread object created, about to start... [2] Thread started (start() returned). B ...
这清晰表明:start() 返回 ≠ 线程已执行 run() → 更不等于已进入 synchronized print()。
✅ 若需实现“t1 执行完再打印 A”,应使用显式同步工具,例如:
CountDownLatch latch1 = new CountDownLatch(1);
// 在 t1 的 run() 中,print 完后调用:latch1.countDown();
// 主线程中:latch1.await(); System.out.println("A");或者更符合原意的控制流重构——若目标是按创建顺序依次执行各线程的 print,且主线程严格等待每一步,则不应依赖多线程并发,而应直接顺序调用;若必须用线程,则需组合 join() 与明确的阶段信号。
? 总结:
-
synchronized≠ 执行顺序控制,它只解决竞态条件(race condition),不解决时序协调(timing coordination); - 线程启动(
start())与实际执行(run()入口)之间存在不可忽略的调度延迟; - Java 内存模型不保证未同步操作的跨线程可见性与时序,所有非同步的跨线程交互都应视为“竞争态”,行为不可预测;
- 要实现确定性顺序,请使用
CountDownLatch、CyclicBarrier、Semaphore或显式join()+ 阶段拆分,而非依赖synchronized或代码书写顺序。



















