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

Java多线程累加数字:counter达10时循环无法终止问题求助

解决多线程累加计数器超标的问题

嘿,我明白你现在的困惑——第一次搞多线程很容易踩竞态条件的坑!你的核心问题在于while循环的判断和计数器递增这两步不是原子操作,哪怕你给单个方法加了synchronized,也挡不住多个线程同时“钻空子”。

问题根源分析

你看,当counter等于9的时候,可能有好几个线程同时通过了while(counter < 10)的判断,然后它们都会执行getAndIncrement()。等第一个线程把counter加到10之后,其他已经通过判断的线程还是会继续执行递增操作,结果自然就超过10了。

另外,你的addToSum是实例级别的同步方法,但sum是静态变量——这意味着每个calculator实例的锁是独立的,多个线程调用不同实例的addToSum时,依然会有线程安全问题,只是你没注意到而已。

解决方案

方案1:使用原子类(推荐,更简洁)

Java的java.util.concurrent.atomic包提供了原子操作类,能帮你避免手动同步的麻烦。我们用AtomicInteger替换普通的int计数器,它的getAndIncrement()方法本身就是原子的,而且我们可以把循环判断和取值合并成一个原子操作:

修改后的calculator类:

import java.util.concurrent.atomic.AtomicInteger;

public class Calculator implements Runnable {
    static AtomicInteger counter = new AtomicInteger(0);
    static int sum = 0;

    // 静态同步方法,确保sum的累加是线程安全的
    private synchronized static void addToSum(int value) {
        sum += value;
    }

    @Override
    public void run() {
        int tempVal;
        // 原子性地获取并递增计数器,同时判断是否小于10
        while ((tempVal = counter.getAndIncrement()) < 10) {
            System.out.println("temp val : " + tempVal);
            addToSum(tempVal);
            System.out.println("counter is now : " + (tempVal + 1));
        }
    }
}

方案2:手动同步临界区

如果你想手动控制同步逻辑,需要把判断计数器+递增计数器这整个逻辑放到同一个同步块里,确保没有线程能插队:

public class Calculator implements Runnable {
    static int counter = 0;
    static int sum = 0;

    private static void addToSum(int value) {
        // 锁类对象,因为sum是静态变量
        synchronized (Calculator.class) {
            sum += value;
        }
    }

    @Override
    public void run() {
        while (true) {
            int tempVal;
            // 锁类对象,确保判断和递增是原子操作
            synchronized (Calculator.class) {
                if (counter >= 10) {
                    break;
                }
                tempVal = counter;
                counter++;
                System.out.println("counter is now : " + counter);
            }
            System.out.println("temp val : " + tempVal);
            addToSum(tempVal);
        }
    }
}

优化main方法的等待逻辑

你原来的空循环等待线程结束太浪费资源了,用Thread.join()更优雅:

public static void main(String[] args) {
    Calculator[] calc = new Calculator[8];
    Thread[] thread = new Thread[8];
    final long startTime = System.currentTimeMillis();

    for (int i = 0; i < 8; i++) {
        calc[i] = new Calculator();
        thread[i] = new Thread(calc[i]);
        thread[i].start();
    }

    // 等待所有线程结束
    for (Thread t : thread) {
        try {
            t.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    final long endTime = System.currentTimeMillis();
    System.out.println(Calculator.sum);
    System.out.println("Execution time : " + (endTime - startTime)); // 修正了时间计算的顺序
}

关键知识点总结

  • 原子性:涉及共享变量的操作,必须确保整个逻辑块是原子的,不能只同步单个步骤。
  • 静态变量的同步:静态变量属于类,所以同步时要锁类对象(Calculator.class),而不是实例对象。
  • 避免空循环:用join()等待线程结束比空轮询更高效也更规范。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:54:02