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

Java多线程场景下如何安全访问并写入类的ArrayList存储素数?

核心问题排查

  • 区间拆分逻辑完全错误:你当前给所有MyRunnable传入的计算区间都是固定的d-numThreads到d,根本没有按线程拆分不同的计算区间,和你预期的多线程分工完全不符,这也是你输出结果异常的直接原因。
  • 线程创建逻辑错误:不需要遍历[lower, upper)判断i%d==0来创建线程,直接按指定线程数均分区间即可。
  • 冗余锁逻辑:
    1. isPrime是无共享状态的纯计算方法,加synchronized会导致所有线程串行执行素数判断,完全丧失多线程性能优势
    2. 你已经用Collections.synchronizedList包装了ArrayList,其add方法本身线程安全,不需要额外加同步块
    3. 主类和MyRunnable中各定义了一个独立的lock对象,无实际作用
  • 集合初始化遗漏:threads ArrayList没有实例化,运行会抛出空指针异常

修正方案

1. 修正区间拆分与线程创建逻辑

按线程数均分计算区间,每个线程分配独立的计算段,最后一个线程兜底处理边界余数。

2. 清理冗余同步逻辑

删除不需要的锁代码,提升并发效率。

修正后参考代码

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Primes {
    int numThreads;
    List<Integer> primes = Collections.synchronizedList(new ArrayList<>());
    public ArrayList<Thread> threads = new ArrayList<>(); // 初始化线程集合

    public Primes(int numThreads) {
        this.numThreads = numThreads;
    }

    public Primes findPrimes(int lower, int upper) {
        int range = upper - lower;
        int step = range / numThreads;
        // 按线程数拆分区间
        for (int k = 0; k < numThreads; k++) {
            int threadLower = lower + k * step;
            int threadUpper = (k == numThreads - 1) ? upper : lower + (k + 1) * step;
            threads.add(new Thread(new MyRunnable(threadLower, threadUpper, primes)));
        }
        for(Thread t : threads) t.start();

        for(Thread t : threads) {
            try {
                t.join();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        return this;
    }

    public static class MyRunnable implements Runnable {
        private int lower;
        private int upper;
        List<Integer> prime;

        public MyRunnable(int lower, int upper, List<Integer> prime) {
            this.lower = lower;
            this.upper = upper;
            this.prime = prime;
        }
        // 去掉synchronized,纯计算无需同步
        protected boolean isPrime(int number) {
            if (number <= 1)
                return false;
            // 优化判断条件到sqrt(number),提升性能
            for (int i = 2; i <= Math.sqrt(number); i++) {
                if (number % i == 0)
                    return false;
            }
            return true;
        }
        public void run() {
            for (int i=lower; i<upper; i++) {
                if (isPrime(i)){
                    prime.add(i); // synchronizedList的add自带线程安全,无需额外加锁
                }
            }
        }
    }

    // 测试用例示例
    public static void main(String[] args) {
        int threadNum = Integer.parseInt(args[0]);
        Primes primes = new Primes(threadNum);
        primes.findPrimes(0, 1000000);
        System.out.printf("0到1000000范围内共有%d个素数,总和为%d%n", 
            primes.primes.size(), 
            primes.primes.stream().mapToInt(Integer::intValue).sum()
        );
    }
}

额外优化建议

  • 素数判断逻辑优化为循环到Math.sqrt(number)而非number/2,计算效率会提升数倍
  • 批量收集子线程的素数结果后再合并到主集合,可以减少同步操作的开销,适合大计算量场景

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 17:45:08