You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 04:16:31