Java多线程疑问:synchronized块执行后线程调度顺序问题
println always run right after the synchronized block in my threaded Java code? Let's break down what's happening with your code and why you're seeing that consistent output instead of the mixed order you expected.
First, here's your code for reference:
public class Callme { public Callme() { // TODO Auto-generated constructor stub } void callA(String msg) throws InterruptedException { synchronized (this) { System.out.print("["+msg); Thread.sleep(1000); } System.out.println("]"); } void callB(String msg) throws InterruptedException { synchronized (this) { System.out.print("{"+msg); Thread.sleep(1000); } System.out.println("}"); } void callC(String msg) throws InterruptedException { synchronized (this) { System.out.print("("+msg); Thread.sleep(1000); } System.out.println(")"); } } public class Caller implements Runnable { public char msg; public Callme target; public Thread t; public Caller(char msg, Callme target) { this.msg = msg; this.target = target; t= new Thread(this); } @Override public void run() { try { switch (msg) { case '[': target.callA("Hello"); break; case '{': target.callB("Hello"); break; case '(': target.callC("Hello"); break; default: break; } } catch (InterruptedException e) { e.printStackTrace(); } } } // Execution code Callme target = new Callme(); Caller ob1 = new Caller('[', target); Caller ob2 = new Caller('{', target); Caller ob3 = new Caller('(', target); ob1.t.start(); ob2.t.start(); ob3.t.start();
The Core Issue: Thread Scheduling Optimizations
You’re right that your expected output (like [Hello(Hello] ) {Hello}) is theoretically possible, but in practice, the JVM and operating system’s thread scheduling makes this outcome extremely unlikely. Here’s why:
- When a thread exits a
synchronizedblock, it releases the lock onthis—but it doesn’t immediately give up the CPU. Operating systems try to minimize context switches (the process of switching between threads) because they’re computationally expensive. The thread that just finished the synchronized block is already "warm" (loaded into the CPU’s cache), so the OS will almost always let it continue executing the remaining unsynchronized code (theprintln("]")) before waking up and scheduling another waiting thread. - The other threads (
ob2andob3) are waiting to acquire the lock, but even after the lock is released, there’s a tiny delay while the scheduler wakes them up and switches context. In that split second, the original thread has already printed the closing bracket.
How to See Your Expected Output
If you want to force a context switch to trigger the mixed output you’re expecting, add a Thread.yield() call right after the synchronized block in each callX method. This tells the scheduler that the current thread is willing to give up its CPU time:
void callA(String msg) throws InterruptedException { synchronized (this) { System.out.print("["+msg); Thread.sleep(1000); } Thread.yield(); // Voluntarily give up CPU time System.out.println("]"); }
With this change, you’ll start seeing outputs like [Hello(Hello] ) {Hello} much more frequently, as the yield gives waiting threads a chance to acquire the lock and run their synchronized blocks before the first thread prints its closing bracket.
Final Note
Remember that thread scheduling is inherently non-deterministic. Your original code could produce the mixed output under rare circumstances (like if the OS interrupted the thread immediately after releasing the lock), but it’s not behavior you can rely on. The consistent output you’re seeing is just the OS optimizing for performance by avoiding unnecessary context switches.
内容的提问来源于stack exchange,提问作者Chaitanya Chavali

