Java synchronized代码在Mac与Windows平台输出不一致求助
问题原因及解决方法
原因分析
你误解了synchronized的作用:它只保证同一时间只有一个线程能进入被同步的代码块,避免多个线程同时执行call()方法导致输出交叉混乱(比如[Hello[World]]这种情况),但它完全不控制线程的执行顺序。
线程的调度权在操作系统和JVM的线程调度器手里,不同平台(Mac/Windows)的调度策略可能存在差异,就算是同一平台,每次运行时的调度结果也可能不一样。你第一次在Windows上得到“预期顺序”只是巧合,后续Windows也出现乱序,正好说明这一点——synchronized从来没承诺过会保证线程的执行顺序。
你的代码里,三个线程创建后立刻启动,哪个线程先抢到target对象的锁,完全是随机的,所以输出顺序自然不固定。
解决方法
如果需要固定的输出顺序,得手动控制线程的执行流程,这里给两种常用方案:
方案1:用join()强制顺序执行
如果不需要多线程并行,只是要顺序输出,可以让线程按顺序启动,并且等待前一个线程执行完再启动下一个:
class Synch1 { public static void main(String args[]) { Callme target = new Callme(); Caller ob1 = new Caller(target, "Hello"); // 等待第一个线程执行完再创建第二个 try { ob1.t.join(); } catch (InterruptedException e) { System.out.println("Interrupted"); } Caller ob2 = new Caller(target, "Synchronized"); try { ob2.t.join(); } catch (InterruptedException e) { System.out.println("Interrupted"); } Caller ob3 = new Caller(target, "World"); // 等待最后一个线程结束 try { ob3.t.join(); } catch (InterruptedException e) { System.out.println("Interrupted"); } } }
不过这种方式其实把多线程变成了串行,失去了并行的意义,适合单纯需要顺序输出的场景。
方案2:用CountDownLatch协调顺序
如果需要线程先启动,再按指定顺序执行,可以用CountDownLatch来做同步控制:
import java.util.concurrent.CountDownLatch; class Callme { void call(String msg) { System.out.print("[" + msg); try { Thread.sleep(1000); } catch (InterruptedException e) { System.out.println("Interrupted"); } System.out.println("]"); } } class Caller implements Runnable { String msg; Callme target; Thread t; CountDownLatch startLatch; CountDownLatch endLatch; public Caller(Callme targ, String s, CountDownLatch start, CountDownLatch end) { target = targ; msg = s; startLatch = start; endLatch = end; t = new Thread(this); t.start(); } public void run() { try { // 等待启动信号 startLatch.await(); synchronized (target) { target.call(msg); } // 通知当前线程执行完成 endLatch.countDown(); } catch (InterruptedException e) { System.out.println("Interrupted"); } } } class Synch1 { public static void main(String args[]) throws InterruptedException { Callme target = new Callme(); // 第一个线程的结束信号,第二个线程等待它完成 CountDownLatch latch1 = new CountDownLatch(1); // 第二个线程的结束信号,第三个线程等待它完成 CountDownLatch latch2 = new CountDownLatch(1); Caller ob3 = new Caller(target, "World", latch2, new CountDownLatch(1)); Caller ob2 = new Caller(target, "Synchronized", latch1, latch2); Caller ob1 = new Caller(target, "Hello", new CountDownLatch(0), latch1); // 等待所有线程结束 ob1.t.join(); ob2.t.join(); ob3.t.join(); } }
这个方案里,三个线程先启动但都等待各自的启动信号,只有前一个线程执行完成后,才会释放信号让下一个线程执行,从而保证顺序。
内容的提问来源于stack exchange,提问作者legendary91
相关产品推荐
相关产品推荐

