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

Java多线程求[1,n]中3、5、7倍数唯一和的问题排查

问题描述

问题背景

需编写多线程程序找出范围[1, n]内可被3、5或7整除的整数,返回所有唯一整数的和(如15同时是3和5的倍数仅计一次),正整数n>0为输入,可按需创建线程,使用线程池可获额外加分。
示例:输入n=10,输出sum=40,对应数字为3、5、6、7、9、10,和为40。

实现与问题

我编写了Java多线程程序:创建三个线程分别查找可被3、5、7整除的数,存入全局ArrayList,再通过以下代码去重:

Set<Integer> set = new HashSet<>(dividends);
dividends.clear();
dividends.addAll(set);

但测试老师提供的用例时,如n=1000预期结果为272066,实际得到247377,且每次运行结果都不稳定。请问代码存在什么问题,该如何修复?

我的代码

import java.util.*;
public class FindMultiples
{

public static ArrayList<Integer> dividends = new ArrayList<>();
public static int temp = 0;
public static synchronized void increment(){
    dividends.add(temp);
}
public static class thread implements Runnable{

    public int divisor;
    public int n;

    public thread(int n , int divisor){
        this.n=n;
        this.divisor=divisor;
    }

    @Override
    public void run() {

        for (int i=1 ; i<=n ; i++){
            if (i%divisor==0){
                temp=i;
                increment();
            }
        }
    }
}

public int getSum(int n) {
    int sum = 0;
    Thread thread1 = new Thread(new thread(n,3));
    Thread thread2 = new Thread(new thread(n,7));
    Thread thread3 = new Thread(new thread(n,5));
    
    thread3.start();
    thread2.start();
    thread1.start();
    try {
        thread3.join();
        thread2.join();
        thread1.join();
    }catch (InterruptedException e){

    }
    Set<Integer> set = new HashSet<>(dividends);
    dividends.clear();
    dividends.addAll(set);

    for (int i : dividends){
        sum+=i;
    }

    return sum;
}

public static void main(String[] args) {
}
}

问题分析与修复

你的代码主要有这几个问题:

  1. 全局temp变量的竞态条件:多个线程会抢着修改temp的值。比如线程A刚把temp设成15,还没来得及调用increment(),线程B就把temp改成21了,最后increment()添加的是21,15直接丢失——这就是结果每次不一样且偏小的核心原因。
  2. ArrayList本身非线程安全:虽然increment()加了synchronized,但如果后续直接操作dividends仍有线程安全风险,不过这次的主要问题还是temp。
  3. 全局静态集合的复用隐患:如果多次调用getSum(),dividends会残留之前的数据,不过当前单次调用的问题主要由temp导致。

修复方案1:移除全局temp,直接传参

把同步方法改成直接接收要添加的数字,避免多线程争抢修改同一个变量:

public static synchronized void addNumber(int num){
    dividends.add(num);
}

然后在线程的run()方法里直接调用:

if (i%divisor==0){
    addNumber(i);
}

这样就能解决结果不稳定的问题。

修复方案2:用线程安全集合一步去重

既然需要去重,不如直接使用线程安全的Set,比如ConcurrentHashMap.newKeySet(),添加时自动去重,省去后续转Set的步骤:

public static Set<Integer> dividends = ConcurrentHashMap.newKeySet();

同步方法里直接调用dividends.add(num),后续的去重代码也可以直接删掉,效率更高。

加分优化版(线程池+线程安全Set)

符合题目“使用线程池获额外加分”的要求,同时规避全局变量的坑:

import java.util.Set;
import java.util.concurrent.*;

public class FindMultiples {

    public int getSum(int n) {
        // 线程安全的Set,自动去重
        Set<Integer> multiples = ConcurrentHashMap.newKeySet();
        
        // 创建固定大小线程池,对应3个除数的任务
        ExecutorService executor = Executors.newFixedThreadPool(3);
        
        // 提交三个任务,分别查找3、5、7的倍数
        executor.submit(() -> addMultiples(n, 3, multiples));
        executor.submit(() -> addMultiples(n, 5, multiples));
        executor.submit(() -> addMultiples(n, 7, multiples));
        
        // 关闭线程池并等待所有任务完成
        executor.shutdown();
        try {
            executor.awaitTermination(1, TimeUnit.MINUTES);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        
        // 计算总和
        return multiples.stream().mapToInt(Integer::intValue).sum();
    }

    private void addMultiples(int n, int divisor, Set<Integer> multiples) {
        // 直接从除数开始,步长设为除数,减少不必要的取模运算
        for (int i = divisor; i <= n; i += divisor) {
            multiples.add(i);
        }
    }

    public static void main(String[] args) {
        FindMultiples fm = new FindMultiples();
        System.out.println(fm.getSum(10)); // 输出40
        System.out.println(fm.getSum(1000)); // 输出272066
    }
}

这个版本的优势:

  • 线程池管理线程,资源利用率更高,符合加分要求。
  • 线程安全Set自动去重,省去额外去重步骤,提升效率。
  • 循环从除数开始、步长为除数,避免多余的取模运算,性能更好。
  • 无全局静态变量,多次调用getSum()也不会有数据残留问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 09:57:46