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

Executor.submit()返回null引发空指针异常的原因与解决咨询

问题分析与解决

为什么会出现Future为null的情况?

首先明确:ExecutorService.submit()方法永远不会返回null。根据Java规范,只要传入的任务(Callable/Runnable)不为null,submit方法一定会返回一个有效的Future实例,用来跟踪任务执行状态。

你遇到的空指针,根源出在ArrayList的线程不安全特性上:

  • ArrayList不是线程安全集合,当多个线程同时对它进行**写入(add)和读取(遍历)**操作时,会出现并发访问的异常情况。比如在一个线程执行futureList.add(...)的同时,另一个线程正在遍历futureList,此时ArrayList的内部结构可能处于不稳定状态,导致遍历到null元素。
  • 另外也要排查代码中是否有其他地方直接调用futureList.add(null),手动往集合里添加了null值,但根据你提供的代码片段,这种可能性较低。

如何解决这个问题?

针对多线程环境下的集合操作,有两种可靠的处理方式:

1. 使用线程安全的集合替换ArrayList

将futureList的实现改为CopyOnWriteArrayList,它是专门为读多写少场景设计的线程安全集合,遍历操作不会受写入操作影响,也不会抛出并发修改异常:

private List<Future<Boolean>> futureList = new CopyOnWriteArrayList<>();

2. 对集合操作加同步锁

如果不想更换集合,可以在所有操作futureList的地方(包括add元素和遍历元素)添加synchronized锁,保证同一时间只有一个线程能操作集合:

// 添加元素时加锁
synchronized (futureList) {
    futureList.add(crawlerTaskExecutor.submit(() -> fetchDocuments(siteUrl), Boolean.FALSE));
}

// 遍历检查时加锁
private boolean isCrawlerRunning() {
    boolean isRunning = Boolean.FALSE;
    if (Objects.nonNull(futureList)) {
        synchronized (futureList) {
            for (Future<Boolean> future : futureList) {
                if (future != null && !future.isDone()) { // 额外加null判断做兜底
                    isRunning = Boolean.TRUE;
                    break;
                }
            }
        }
    }
    return isRunning;
}

另外,在遍历的时候可以额外增加future != null的判断,作为兜底防护,避免极端情况下的空指针。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 05:53:03