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

线程同步疑问:为何不同代码执行下interview.b输出值不同?

问题:synchronized方法执行导致的变量值差异分析

测试代码

public class Interview implements Runnable {
    int b = 100;
    public synchronized void m1() throws Exception {
        //System.out.println("-----");
        b = 1000;
        Thread.sleep(5000);
        System.out.println("b = " + b);
    }

    public synchronized void m2() throws Exception {
        Thread.sleep(2500);
        b = 2000;
    }

    @Override
    public void run() {
        try {
            m1();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) throws Exception {
        Interview interview = new Interview();
        Thread thread = new Thread(interview);
        thread.start();
        interview.m2();
        System.out.println(interview.b);
    }
}

两种执行情况的输出

情况1:注释掉m1中的System.out.println("-----");

1000  
b = 1000   

情况2:不注释该语句

2000   
-----    
b = 1000    

差异原因分析

这本质是线程启动的不确定性加上synchronized锁的竞争,再配合System.out.println的同步特性共同导致的:

  1. 首先,thread.start()只是通知JVM启动新线程,不是立刻执行,主线程会继续往下调用interview.m2()。
  2. m1和m2都是用synchronized修饰的实例方法,锁定的是同一个interview对象,所以同一时间只有一个线程能执行其中任意一个方法。

注释掉println的场景:

新线程启动后,因为m1的代码更简洁(没有额外的IO和同步操作),JVM大概率会先调度它抢到锁:

  • 新线程进入m1,先把b改成1000,然后睡5秒;
  • 主线程调用m2时发现锁被占,只能阻塞等待;
  • 新线程睡醒后打印b=1000,释放锁;
  • 主线程拿到锁进入m2,睡2.5秒后把b改成2000,但主线程的System.out.println(interview.b);是在调用m2之后立刻执行的——当时m2还在阻塞状态,所以输出的是新线程已经修改过的1000。

不注释println的场景:

System.out.println内部自带synchronized同步块,还涉及IO操作,会给新线程的执行带来额外延迟:

  • 主线程在新线程还没抢到锁的时候,先进入m2并拿到锁;
  • 主线程睡2.5秒后把b改成2000,执行完m2释放锁;
  • 主线程立刻执行System.out.println(interview.b);,输出2000;
  • 这时候新线程终于抢到锁进入m1,先打印-----,把b改成1000,睡5秒后打印b=1000。

总结来说,两种情况的差异就是**System.out.println的存在改变了线程调度的时机,导致哪个线程先抢到锁的结果不同**,最终影响了主线程输出b时的取值。


内容的提问来源于stack exchange,提问作者苏苏沐哲

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 22:10:00