Java多线程执行时长异常求助:线程越多耗时反而增加
首先得戳破你的误区:你当前的多线程写法根本不是并行加速,而是让多个线程重复做完全相同的工作——每个线程都要处理全部50个矩阵,这相当于把单线程的工作量直接放大了n倍(2线程就是跑100次所有矩阵,3线程150次),CPU被重复计算占满,再加上上下文切换和缓存冲突的开销,耗时自然越来越高。
具体原因拆解
重复计算导致的资源竞争
你原本期望的是“多线程并行处理任务”,但实际代码里每个线程都在执行和单线程完全一样的遍历逻辑。当多个线程同时抢占CPU核心时,操作系统需要不断切换线程上下文,这个过程会消耗大量CPU资源,直接拖慢整体执行速度。缓存颠簸(Cache Thrashing)
Floyd-Warshall是内存密集型算法,需要频繁读写矩阵数据。当多个线程同时访问相同的矩阵内存区域时,CPU缓存行会被不同核心频繁地同步、失效——比如线程1刚把某块数据加载到缓存,线程2又修改了这块数据,导致线程1的缓存失效,不得不重新从内存读取。这种缓存的频繁失效会极大降低内存访问效率,线程越多,冲突越严重。线程数超过实际可用核心的开销
如果你的CPU核心数少于线程数(比如4核开3线程,看起来没问题,但每个线程都在满负荷计算),核心会被多个线程抢占,上下文切换的成本会急剧上升,进一步加剧性能下降。
正确的并行写法:拆分任务而非重复任务
要实现真正的并行加速,你需要把任务拆分,让每个线程只处理一部分矩阵,而不是全部。比如50个矩阵,2线程各处理25个,3线程分成17、17、16份。修改后的代码如下:
private static void runThreads(List<int[][]> graphs, int nThreads) throws InterruptedException { ExecutorService executor = Executors.newFixedThreadPool(nThreads); Collection<Callable<String>> callables = new ArrayList<>(); // 把矩阵列表拆分成nThreads个批次 int batchSize = graphs.size() / nThreads; for(int i = 0; i < nThreads; i++) { final int startIdx = i * batchSize; // 最后一个线程处理剩余的所有矩阵 final int endIdx = (i == nThreads - 1) ? graphs.size() : (i + 1) * batchSize; final List<int[][]> assignedGraphs = graphs.subList(startIdx, endIdx); callables.add(() -> { for(int[][] graph : assignedGraphs) { FloydWarshall f = new FloydWarshall(graph); f.checkConsistency(); } return null; }); } long startTime = System.currentTimeMillis(); executor.invokeAll(callables); executor.shutdown(); long totalTime = System.currentTimeMillis() - startTime; System.out.println("Total time: " + totalTime + " ms"); }
额外优化提示
- 线程数建议设置为CPU核心数或核心数+1(利用超线程),不要盲目增加线程数,超过核心数后上下文切换的开销会抵消并行收益。
- 检查
FloydWarshall类,确保没有使用共享的可变静态变量——即使拆分了任务,共享可变状态也可能导致隐形的同步开销或逻辑错误。 - 如果单个矩阵的计算量极大(比如1000×1000),可以考虑把单个矩阵的Floyd-Warshall计算也并行化(比如拆分k循环到不同线程),但这需要注意数据同步(比如使用
AtomicInteger或者分段锁),实现起来更复杂。
修改后再测试,你应该能看到线程数增加时,总耗时明显下降(接近单线程耗时的1/n,忽略线程启动和切换的微小开销)。
内容的提问来源于stack exchange,提问作者John F

