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

Java多线程求和ArrayList触发ConcurrentModificationException求助

问题分析与解决方案

嗨,我来帮你捋一捋你遇到的问题哈~你现在碰到两个核心问题:ConcurrentModificationException异常和求和结果不符合预期,咱们一个个拆解清楚:

为什么会出现ConcurrentModificationException?

你给populate()和sum()加了synchronized,但这俩是实例方法,锁的是当前Tst对象本身。而你在main方法里new了三个完全不同的Tst实例,这意味着三个线程各自拿着不同的锁,完全起不到互斥的作用!

当其中一个线程在调用sum()遍历list的时候,另外的线程可能还在调用populate()往list里加元素——而ArrayList本身不是线程安全的容器,在遍历过程中如果有其他线程修改它的结构(add/remove等),就会触发这个并发修改异常。

为什么求和结果不对?

静态变量sum被三个线程同时修改,而且sum+=ger这个操作不是原子性的(会拆分成读-加-写三步),多个线程同时操作会导致数据覆盖,所以最终结果会比预期的500500小。


修正后的代码

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Tst extends Thread {
    // 用AtomicInteger保证sum的原子性修改,避免多线程数据覆盖
    private static final AtomicInteger sum = new AtomicInteger(0);
    private final int MOD = 3;
    private final int compare;
    private static final int LIMIT = 1000;
    // 把ArrayList包装成线程安全的容器,解决并发修改问题
    private static final List<Integer> list = Collections.synchronizedList(new ArrayList<>());

    public Tst(int compare) {
        this.compare = compare;
    }

    public void populate() throws InterruptedException {
        // 用类锁保证所有线程操作list时互斥,避免遍历和修改同时发生
        synchronized (Tst.class) {
            for (int i = 0; i <= LIMIT; i++) {
                if (i % MOD == compare) {
                    list.add(i);
                }
            }
        }
    }

    public void sum() throws InterruptedException {
        // 遍历线程安全容器时也加类锁,彻底避免并发修改异常
        synchronized (Tst.class) {
            for (Integer num : list) {
                if (num % MOD == compare) {
                    sum.addAndGet(num);
                }
            }
        }
    }

    @Override
    public void run() {
        try {
            populate();
            sum();
            System.out.println("线程" + compare + "执行后当前总和:" + sum.get());
        } catch (InterruptedException ex) {
            Logger.getLogger(Tst.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Tst tst1 = new Tst(0);
        Tst tst2 = new Tst(1);
        Tst tst3 = new Tst(2);

        tst1.start();
        tst2.start();
        tst3.start();

        // 等待所有线程执行完毕,再打印最终正确总和
        tst1.join();
        tst2.join();
        tst3.join();

        System.out.println("最终总和:" + sum.get());
    }
}

关键修改点说明

  • 线程安全容器:用Collections.synchronizedList把普通ArrayList包装成线程安全的容器,也可以用CopyOnWriteArrayList(适合读多写少的场景,遍历自带线程安全)。
  • 统一类锁:用synchronized (Tst.class)加类锁,保证三个线程操作list时完全互斥,彻底避免遍历和修改同时进行的情况。
  • 原子性求和:把int sum换成AtomicInteger,用addAndGet()方法保证修改操作的原子性,解决多线程同时修改导致的数据丢失问题。
  • 等待线程完成:在main方法里调用join(),等三个线程都执行完再打印最终总和,确保看到的是完整计算后的结果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 09:00:30