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

Java ExecutorService多线程任务未全部执行问题求助

多线程环境下ExecutorService任务执行计数异常问题解决

问题描述

通过Java的ExecutorService执行多组列表的迭代任务,总计需调用1890万次calculateRecords方法。测试发现:

  • 单线程运行时,int类型变量count的统计值符合预期;
  • 多线程运行时,count始终无法达到预期值,任务看似提前终止。

相关代码如下:

package main;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Test
{
    public static int count = 0;
    private static final DecimalFormat decimalFormat = new DecimalFormat("#.###");
    
    public static void main(String[] args)
    {
        int aStart = 140;
        int aEnd = 400;
        int aIncrement = 20;
        
        double bStart = 9;
        double bEnd = 27;
        double bIncrement = 0.75;
        
        int cStart = 14;
        int cEnd = 18;
        int cIncrement = 1;
        
        int dStart = 90;
        int dEnd = 180;
        int dIncrement = 30;
        
        int eStart = 60;
        int eEnd = 120;
        int eIncrement = 30;
        
        int fStart = 130;
        int fEnd = 250;
        int fIncrement = 30;
        
        double gStart = 5.5;
        double gEnd = 6.7;
        double gIncrement = 0.3;
        
        double hStart = 2.8;
        double hEnd = 4.2;
        double hIncrement = 0.2;
        
        double iStart = 2.6;
        double iEnd = 4;
        double iIncrement = 0.2;
        
        List<Integer> listA = list(aStart, aEnd, aIncrement);
        List<Double>  listB = list(bStart, bEnd, bIncrement);
        List<Integer> listC = list(cStart, cEnd, cIncrement);
        List<Integer> listD = list(dStart, dEnd, dIncrement);
        List<Integer> listE = list(eStart, eEnd, eIncrement);
        List<Integer> listF = list(fStart, fEnd, fIncrement);
        List<Double>  listG = list(gStart, gEnd, gIncrement);
        List<Double>  listH = list(hStart, hEnd, hIncrement);
        List<Double>  listI = list(iStart, iEnd, iIncrement);
        
        final int MAX_THREADS = 2;
        ExecutorService executor = Executors.newFixedThreadPool(MAX_THREADS);
        
        List<String> row = new ArrayList<>();
        
        for (int a : listA)
        {
            for (double b : listB)
            {
                for (int c : listC)
                {
                    for (int d : listD)
                    {
                        for (int e : listE)
                        {
                            for (int f : listF)
                            {
                                for (double g : listG)
                                {
                                    for (double h : listH)
                                    {
                                        for (double i : listI)
                                        {
                                            if (i < h) 
                                            {
                                                executor.submit(() -> row.addAll(calculateRecords("C://CSV", a, b, c, d, e, f, g, h, i)));
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        
        executor.shutdown();
        try
        {
            while (!executor.isTerminated())
            {
                // Wait for all tasks to complete
            }
            System.out.println("TEST COMPLETE");
        } catch (Exception e)
        {
            System.out.println("Thread execution interrupted.");
            Thread.currentThread().interrupt();
        }

        System.out.println(count);
    }
    
    public static ArrayList<Integer> list(int startPoint, int endPoint, int incrementPoint)
    {
        ArrayList<Integer> a = new ArrayList<>();
        for (int queryPoint = startPoint; queryPoint <= endPoint; queryPoint += incrementPoint)
        {
            a.add(queryPoint);
        }
        
        return a;
    }
    
    public static ArrayList<Double> list(double startPoint, double endPoint, double incrementPoint)
    {
        ArrayList<Double> a = new ArrayList<>();
        double tolerance = incrementPoint / 2.0;
        for (double queryPoint = startPoint; queryPoint <= endPoint + tolerance; queryPoint += incrementPoint)
        {
            double formatPoint = Double.parseDouble(decimalFormat.format(queryPoint));
            a.add(formatPoint);
        }
        return a;
    }
    
    public static List<String> calculateRecords(String directory, int a, double b, int c, int d, int e, int f, double g, double h, double i)
    {
        List<String> listC = new ArrayList<>();
        count++;
        return listC;
    }
}

问题根源

  1. 非线程安全的计数器:count是普通int类型,count++操作并非原子性(包含读取、加1、写入三个步骤),多线程并发执行时会出现竞态条件,导致计数丢失,最终统计值低于预期。
  2. 非线程安全的集合操作:row是ArrayList,不支持并发修改。多个线程同时调用row.addAll()会触发ConcurrentModificationException,异常可能导致任务提前终止,进一步影响计数准确性。

解决方案

1. 替换为线程安全的计数器

使用java.util.concurrent.atomic.AtomicInteger替代int类型的count,它提供原子性的自增操作,避免多线程下的计数丢失。

2. 使用线程安全的集合

将ArrayList<String> row替换为线程安全的集合,比如CopyOnWriteArrayList<String>,或者在主线程汇总任务结果(更高效),避免并发修改异常。

3. 优化等待任务完成的逻辑

使用executor.awaitTermination()替代空循环等待,更优雅且节省资源。

修改后的代码示例

package main;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class Test
{
    // 替换为线程安全的原子计数器
    public static AtomicInteger count = new AtomicInteger(0);
    private static final DecimalFormat decimalFormat = new DecimalFormat("#.###");
    
    public static void main(String[] args)
    {
        int aStart = 140;
        int aEnd = 400;
        int aIncrement = 20;
        
        double bStart = 9;
        double bEnd = 27;
        double bIncrement = 0.75;
        
        int cStart = 14;
        int cEnd = 18;
        int cIncrement = 1;
        
        int dStart = 90;
        int dEnd = 180;
        int dIncrement = 30;
        
        int eStart = 60;
        int eEnd = 120;
        int eIncrement = 30;
        
        int fStart = 130;
        int fEnd = 250;
        int fIncrement = 30;
        
        double gStart = 5.5;
        double gEnd = 6.7;
        double gIncrement = 0.3;
        
        double hStart = 2.8;
        double hEnd = 4.2;
        double hIncrement = 0.2;
        
        double iStart = 2.6;
        double iEnd = 4;
        double iIncrement = 0.2;
        
        List<Integer> listA = list(aStart, aEnd, aIncrement);
        List<Double>  listB = list(bStart, bEnd, bIncrement);
        List<Integer> listC = list(cStart, cEnd, cIncrement);
        List<Integer> listD = list(dStart, dEnd, dIncrement);
        List<Integer> listE = list(eStart, eEnd, eIncrement);
        List<Integer> listF = list(fStart, fEnd, fIncrement);
        List<Double>  listG = list(gStart, gEnd, gIncrement);
        List<Double>  listH = list(hStart, hEnd, hIncrement);
        List<Double>  listI = list(iStart, iEnd, iIncrement);
        
        final int MAX_THREADS = 2;
        ExecutorService executor = Executors.newFixedThreadPool(MAX_THREADS);
        
        // 使用线程安全集合,或改为主线程汇总结果
        List<String> row = new java.util.concurrent.CopyOnWriteArrayList<>();
        
        for (int a : listA)
        {
            for (double b : listB)
            {
                for (int c : listC)
                {
                    for (int d : listD)
                    {
                        for (int e : listE)
                        {
                            for (int f : listF)
                            {
                                for (double g : listG)
                                {
                                    for (double h : listH)
                                    {
                                        for (double i : listI)
                                        {
                                            if (i < h) 
                                            {
                                                // 捕获任务中的异常,避免影响其他任务
                                                executor.submit(() -> {
                                                    try {
                                                        row.addAll(calculateRecords("C://CSV", a, b, c, d, e, f, g, h, i));
                                                    } catch (Exception ex) {
                                                        ex.printStackTrace();
                                                    }
                                                });
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        
        executor.shutdown();
        try
        {
            // 等待所有任务完成,超时时间可根据实际情况调整
            if (!executor.awaitTermination(1, TimeUnit.HOURS)) {
                executor.shutdownNow();
            }
            System.out.println("TEST COMPLETE");
        } catch (InterruptedException e)
        {
            executor.shutdownNow();
            Thread.currentThread().interrupt();
            System.out.println("Thread execution interrupted.");
        }

        System.out.println(count.get());
    }
    
    public static ArrayList<Integer> list(int startPoint, int endPoint, int incrementPoint)
    {
        ArrayList<Integer> a = new ArrayList<>();
        for (int queryPoint = startPoint; queryPoint <= endPoint; queryPoint += incrementPoint)
        {
            a.add(queryPoint);
        }
        
        return a;
    }
    
    public static ArrayList<Double> list(double startPoint, double endPoint, double incrementPoint)
    {
        ArrayList<Double> a = new ArrayList<>();
        double tolerance = incrementPoint / 2.0;
        for (double queryPoint = startPoint; queryPoint <= endPoint + tolerance; queryPoint += incrementPoint)
        {
            double formatPoint = Double.parseDouble(decimalFormat.format(queryPoint));
            a.add(formatPoint);
        }
        return a;
    }
    
    public static List<String> calculateRecords(String directory, int a, double b, int c, int d, int e, int f, double g, double h, double i)
    {
        List<String> listC = new ArrayList<>();
        // 原子自增操作
        count.incrementAndGet();
        return listC;
    }
}

额外优化建议

  • 批量提交任务:嵌套循环直接提交1890万次任务会占用大量内存,可将任务分批打包提交,减少内存消耗。
  • 使用自定义线程池:Executors.newFixedThreadPool的队列是无界的,大量任务可能导致OOM,建议使用ThreadPoolExecutor自定义队列大小,配合拒绝策略。
  • 避免外部变量捕获问题:原代码中lambda捕获的循环变量在Java 8+中是final或有效final的,但如果循环变量是可变类型需注意,当前代码中基本类型没问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 19:24:50