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

