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

Java多线程使用join()计算1-100000求和结果异常问题排查

问题分析与解决方案

这是典型的多线程竞态条件问题,核心原因是共享变量的非原子操作导致的线程安全问题,我来给你拆解分析下:

为什么会出现错误结果?

你的Calculation类中的total是多个线程共享的变量,而total += i这个操作不是原子操作——它实际上被拆分成了三个步骤:

  1. 读取total的当前值
  2. 将读取的值与i相加,得到新值
  3. 将新值写回total

当两个线程同时执行这三步时,就会出现“覆盖”问题:比如线程A读取了total=100,线程B也同时读取了total=100;线程A计算后写回100+5=105,线程B计算后写回100+3=103,最终total的值是103,而不是预期的108。这种情况会随着计算量增大(比如1到100000)变得频繁,导致结果偏小且每次都不同;而小数值计算时,线程执行速度快,调度冲突概率低,所以不容易暴露问题。单线程时没有竞争,自然结果正确。

几种可行的解决方案

方案1:给共享方法加同步锁

给calcSum方法加上synchronized修饰符,确保同一时间只有一个线程能执行这个方法,避免竞态条件:

class Calculation { 
    private long total=0; 
    public synchronized void calcSum(long start, long end) { 
        long i = start;
        for( ;i<=end; i++) { 
            total += i; 
        } 
    } 
    public long getTotal() { 
        return total; 
    } 
}

这种方式简单直接,适合小规模计算场景,缺点是会带来一定的锁竞争开销,但对于你的需求来说完全够用。

方案2:使用原子类替代普通变量

Java提供了java.util.concurrent.atomic包下的原子类,它们的操作都是原子性的,不需要手动加锁。把total换成AtomicLong,用addAndGet方法替代+=操作:

import java.util.concurrent.atomic.AtomicLong;

class Calculation { 
    private AtomicLong total = new AtomicLong(0); 
    public void calcSum(long start, long end) { 
        long i = start;
        for( ;i<=end; i++) {
            total.addAndGet(i);
        }
    } 
    public long getTotal() { 
        return total.get(); 
    } 
}

这种方式比synchronized更轻量,适合单个变量的原子更新场景。

方案3:线程私有局部和,最后合并

让每个线程计算自己负责区间的局部和,最后在主线程中把两个局部和相加,完全避免共享变量的修改:

class CalculatorThread extends Thread{ 
    private long start; 
    private long end; 
    private long threadSum = 0; // 线程私有变量,无竞争

    public CalculatorThread(long start, long end) { 
        this.start = start; 
        this.end = end; 
    } 

    @Override 
    public void run() { 
        long i = start;
        for( ;i<=end; i++) {
            threadSum += i;
        }
    } 

    public long getThreadSum() {
        return threadSum;
    }
}

public class ParallelTest { 
    public static void main(String[] args) throws InterruptedException { 
        int start = 1; 
        int end = 100000; 

        CalculatorThread ct1 = new CalculatorThread(start, end/2); 
        CalculatorThread ct2 = new CalculatorThread( (end/2) + 1, end); 

        ct1.start(); 
        ct2.start(); 
        ct1.join(); 
        ct2.join(); 

        System.out.println(ct1.getThreadSum() + ct2.getThreadSum()); 
    } 
}

这种方式性能最优,因为没有任何锁竞争,是多线程计算场景中推荐的思路——尽量减少共享状态,让线程各自处理独立的任务,最后合并结果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:13:31