如何修复多线程矩阵生成应用中的NullPointerException
修复多线程矩阵生成中的NullPointerException问题
我来帮你拆解下这个空指针异常的根源,然后给出具体的修复方案:
首先看堆栈信息,异常出现在MultiTaskGenerator.java:78的tasks[currentIndex].getAutomat()这行——这说明**tasks[currentIndex]的值是null**。我们来分析为什么会出现这种情况:
问题根源
- 线程异步执行的时序问题:你在
initThreads里创建线程并启动后,主线程立刻进入循环检查线程状态,但子线程的执行是异步的——很可能主线程已经在尝试访问tasks元素时,子线程还没来得及执行tasks[j] = new GeneratorTask();这行赋值代码。 - 超时处理的逻辑漏洞:当线程因为超时被中断时,你直接调用
tasks[taskIndex].stopGeneration(true),但如果此时tasks[taskIndex]还没被初始化(线程还没走到赋值步骤),这行也会触发空指针。 initNewThread方法的冗余逻辑:这个方法里的循环完全没必要,反而容易引入不必要的分支问题。
具体修复步骤
1. 访问tasks前先做空判断
在处理已结束线程的逻辑中,先检查tasks[currentIndex]是否为null,避免直接调用方法:
if (!mapEntry.getKey().isAlive()) { if (automatReady == BASE_SIZE) continue; int currentIndex = mapEntry.getValue().getIndex(); // 新增:如果任务未初始化,跳过并重启线程 if (tasks[currentIndex] == null) { System.out.println("Thread №" + currentIndex + " finished but task not initialized, restarting..."); iterator.remove(); initNewThread(currentIndex); startThreads(); continue; } BASE[automatReady] = tasks[currentIndex].getAutomat(); System.out.println("Добавлен автомат № " + automatReady); automatReady++; System.out.println("Thread №" + mapEntry.getValue().getIndex() + " finished successfully"); iterator.remove(); initNewThread(mapEntry.getValue().getIndex()); startThreads(); System.out.println("Thread №" + mapEntry.getValue().getIndex() + " started"); }
2. 修复超时处理中的空指针
在调用stopGeneration前,先判断任务是否已初始化:
else { if ((System.currentTimeMillis() - mapEntry.getValue().getTimeCreated()) < (THREAD_LIVE_TIME_SEC * 1000)) continue; int taskIndex = mapEntry.getValue().getIndex(); // 新增:仅当任务已初始化时才调用停止方法 if (tasks[taskIndex] != null) { tasks[taskIndex].stopGeneration(true); } System.out.println("Thread №" + taskIndex + " stopped"); mapEntry.getKey().interrupt(); iterator.remove(); initNewThread(taskIndex); startThreads(); }
3. 简化initNewThread方法
去掉多余的循环,直接针对传入的索引创建线程:
private void initNewThread(int index) { System.out.println("Thread №" + index + " restarted"); threads.put(new Thread(() -> { tasks[index] = new GeneratorTask(); }), new ThreadInformation(System.currentTimeMillis(), index)); }
4. 优化线程初始化后的等待(可选)
在initThreads后,给子线程一点初始化时间,避免一开始就输出一堆null:
private void getThreadResult() { initThreads(); // 新增:短暂等待子线程初始化任务 try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } for (int i = 0; i < tasks.length; i++) { System.out.println(tasks[i]); } // ... 后续原有循环逻辑 }
额外优化建议
- 用
CompletableFuture或CountDownLatch替代主线程的无限轮询,减少CPU消耗,让代码更优雅。 - 把
tasks换成AtomicReferenceArray<GeneratorTask>,保证数组元素的线程可见性(volatile数组仅保证数组引用的可见性,元素修改不一定能立刻被其他线程感知)。 - 对
BASE数组的修改加锁,或者用线程安全的集合存储生成的矩阵,避免潜在的并发问题。
内容的提问来源于stack exchange,提问作者Y2Kot
相关产品推荐
相关产品推荐

