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

Spring Boot多线程查询PostgreSQL超时并抛类型转换异常求助

我来帮你拆解一下你遇到的两个核心问题:ClassCastException异常和40秒查询超时,咱们一步步定位修复:


1. 先解决ClassCastException异常

从报错信息来看,是把ArrayList强制转换成Object[]时失败了,根源在你的UserService.callUser()方法的结果合并逻辑里:

for (Future<List<Object>> f : futures) {
    if (i == 0) {
        b = new ArrayList<>(f.get()); // 这里f.get()返回的是单条查询结果:List<Object>(每个元素是User记录的Object数组)
    } else {
        b.add(f.get()); // 错误:把整个List<Object>作为元素添加到b中,导致b的元素变成了List类型
    }
    i++;
}

当你在Controller里尝试把这些元素当成Object[](原生SQL查询的单条记录是Object数组)处理时,自然会抛出类型转换异常。

修复方法:不管是第一次还是后续查询,都用addAll把查询结果的每个元素合并到总列表中:

List<Object> b = new ArrayList<>();
for (Future<List<Object>> f : futures) {
    b.addAll(f.get()); // 将每条查询结果的所有记录合并到总列表
}

2. 解决40秒超时问题

这个问题有两个关键原因:

(1)ExecutorService未关闭,导致awaitTermination阻塞满40秒

你调用了executorService.awaitTermination(40, TimeUnit.SECONDS),但没有先调用executorService.shutdown()。awaitTermination的作用是等待线程池终止,如果不调用shutdown,线程池会一直处于运行状态,不会主动终止,所以会硬生生等满40秒才继续执行后续代码。

修复:提交完所有任务后立即关闭线程池,再设置合理的等待超时时间:

// 提交所有任务后
executorService.shutdown(); // 不再接受新任务,准备终止
boolean isTerminated = executorService.awaitTermination(10, TimeUnit.SECONDS); // 缩短等待时间,比如10秒
if (!isTerminated) {
    executorService.shutdownNow(); // 超时则强制关闭线程池
}

(2)EntityManager线程不安全,多线程共享导致阻塞

Spring默认注入的EntityManager是单例且线程不安全的(它是绑定到当前线程事务上下文的代理)。你在多个Callable线程中共享同一个entityManager实例,会导致连接池阻塞、事务上下文混乱,这才是查询缓慢的核心原因。

修复方法:改用EntityManagerFactory创建线程私有的EntityManager实例:

@Service
public class DataServiceImpl {
    @Autowired
    private EntityManagerFactory emf;

    public List<Object> showUser(int firstParam, int secondParam) {
        EntityManager entityManager = emf.createEntityManager();
        try {
            String sql = "select * from sunny.user where id between ?1 and ?2";
            Query query = entityManager.createNativeQuery(sql);
            query.setParameter(1, firstParam);
            query.setParameter(2, secondParam);
            return query.getResultList();
        } finally {
            if (entityManager.isOpen()) {
                entityManager.close(); // 用完必须关闭,释放数据库连接
            }
        }
    }
}

另外,你原来的参数循环逻辑可以优化得更清晰:

// 替代原来的参数计算逻辑
for (int id = 1; id <=5; id++) {
    int start = id;
    int end = id +1;
    Callable<List<Object>> callable = new ProcessingData(start, end);
    futures.add(executorService.submit(callable));
}

修复后的核心代码片段

修改后的UserService.callUser()

public List<Object> callUser() throws InterruptedException, ExecutionException {
    List<Future<List<Object>>> futures = new ArrayList<>();
    ExecutorService executorService = Executors.newCachedThreadPool();

    // 清晰的参数生成逻辑
    for (int id = 1; id <=5; id++) {
        int start = id;
        int end = id +1;
        Callable<List<Object>> callable = new ProcessingData(start, end);
        futures.add(executorService.submit(callable));
    }

    // 正确关闭线程池并等待任务完成
    executorService.shutdown();
    if (!executorService.awaitTermination(10, TimeUnit.SECONDS)) {
        executorService.shutdownNow();
    }

    // 正确合并查询结果
    List<Object> result = new ArrayList<>();
    for (Future<List<Object>> f : futures) {
        result.addAll(f.get());
    }
    return result;
}

这样修改后,既解决了类型转换异常,又修复了线程安全导致的超时问题,查询速度会恢复正常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:08:29