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

从非同步方法调用同步方法引发的线程安全问题:为何同步代码块仍无法保证多线程下total_home数值的唯一性?

问题根源分析

咱们先拆解代码里的关键部分,就能明白为什么会出现重复值了:

1. 先确认:counter()方法是线程安全的

counter()是static synchronized方法,它的锁是Country.class这个类对象,所以多个线程调用这个方法时,一定会排队执行++home操作,每次调用确实会返回唯一递增的数值,这部分逻辑没问题。

2. 问题出在getTotal()方法和total_home变量上

你看,total_home是Country的实例变量(没有static修饰),虽然Main里只创建了一个Country实例,两个线程共享这个实例的total_home,但getTotal()方法没有任何同步措施,里面的两步操作:

total_home = counter();
System.out.println(total_home);

这两步不是原子的!也就是说,线程A执行完赋值操作后,还没来得及打印,线程B可能已经完成了新的赋值,直接覆盖了total_home的值。

举个具体的时序例子,一眼就能懂:

  • 线程A调用getTotal():拿到counter()返回的1,把total_home设为1,但还没执行打印
  • 线程B此时也调用getTotal():拿到counter()返回的2,把total_home覆盖成2,然后执行打印,输出2
  • 线程A终于轮到执行打印了,但此时total_home已经是2了,所以它也输出2

结果就是:本该输出的1没了,出现了两个2,看起来数值不唯一。

3. 本质原因

我们只保证了「生成唯一递增数」的过程是线程安全的,但没保证「把这个数赋值给total_home并打印」的过程是原子的。多个线程可以同时操作同一个total_home变量,后执行的赋值会覆盖先执行的赋值结果,导致先执行的线程打印的是被覆盖后的值。


修复方案

有两种简单的修复方式,任选其一就行:

方案一:让getTotal()成为同步方法

给getTotal()加上synchronized修饰,这样同一个Country实例的getTotal()只能被一个线程执行,赋值和打印就变成原子操作了:

public synchronized void getTotal() {
    total_home = counter();
    System.out.println(total_home);
}

方案二:用局部变量暂存counter的返回值

把counter()的返回值先存在一个局部变量里,再赋值和打印,这样每个线程拿到的唯一值会存在自己的栈帧里,不会被其他线程覆盖:

public void getTotal() {
    int temp = counter();
    total_home = temp;
    System.out.println(temp);
}

这样修改后,不管运行多少次,输出的数值都会是唯一递增的。

内容的提问来源于stack exchange,提问作者krishna

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 17:37:42