如何大幅提升调用存储过程场景下ThreadPoolExecutor的执行性能
ThreadPoolExecutor 性能优化解决方案
原有问题代码
BlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(100, true); RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy(); ExecutorService executor = new ThreadPoolExecutor(4, 10, 0L, TimeUnit.MILLISECONDS, queue,handler); executor.execute(new Runnable() { @Override public void run() { for(looping arraylist){ operations related to calling proc... } } }); executor.shutdown(); while (executor.isTerminated() == false){ try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } }
核心性能瓶颈分析
- 当前多线程方案完全没有生效:整个逻辑只提交了1个Runnable任务,所有遍历、调用存储过程的操作都在单线程串行执行,线程池的多线程能力完全没有被利用,这是耗时过高的最主要原因
- 线程池参数设置和业务场景不匹配:IO密集型场景(调用存储过程属于数据库IO操作)的线程数设置规则和CPU密集型不同,现有参数没有适配业务特征
- 任务结束等待逻辑效率低:循环判断线程池状态加固定sleep的写法会产生不必要的调度开销
可行优化方案
任务拆分(核心优化,可直接降低80%以上耗时)
- 遍历的ArrayList元素之间无依赖的话,直接把每个元素对应的存储过程调用拆成独立任务提交到线程池,所有子任务并行执行
- 两个需要拉取数据的存储过程如果没有逻辑依赖,直接拆成两个独立任务并行执行,执行完成后再合并结果
- 元素量过大时可按批次拆分,比如每20个元素对应一个子任务,平衡任务调度开销和并行效率
线程池参数调优
- 线程数调整:IO密集型场景推荐将核心线程数、最大线程数设置为CPU核心数的2~4倍,也可以和数据库连接池的最大连接数保持一致,避免线程数超过数据库连接数导致大量线程阻塞等待连接
- 队列调整:如果任务拆分后数量可控,可以替换为
SynchronousQueue,任务提交后直接递给线程执行,不需要缓存队列,能更快触发线程池扩容到最大线程数 - 拒绝策略调整:如果可以控制任务提交速率,替换为
ThreadPoolExecutor.AbortPolicy,出现任务过载时直接抛出异常快速感知问题,避免CallerRunsPolicy占用主线程执行任务拖慢整体流程
逻辑细节优化
- 替换原有循环等待逻辑,使用
executor.awaitTermination(合理超时时间, TimeUnit.时间单位),底层基于等待通知机制实现,比循环判断加sleep的写法效率高很多 - 如果需要获取存储过程返回的结果,改用
submit()提交Callable任务,通过Future或者CompletableFuture批量收集结果,避免自己维护结果容器的线程安全问题
数据库配套优化
- 确保数据库连接池的最大连接数≥线程池最大线程数,避免线程拿不到连接空等
- 存储过程本身做优化,减少不必要的关联查询、增加索引,大数据量拉取时分页返回避免单次IO耗时过长
- 拉取数据时配置JDBC的
fetchSize参数,分批从服务端拉取结果,减少网络传输和客户端内存占用
内容的提问来源于stack exchange,提问作者Rohan
相关产品推荐
相关产品推荐

