多线程问题:线程完成后获取执行结果
问题分析与解决方案
你的代码核心问题在于主线程和Worker线程的执行完全不同步,这直接导致了array为空:
- 当你调用
pool.execute(littleDwarfWorker)时,只是把任务丢给线程池去排队或执行,主线程会立刻跳去执行finally块——这时候Worker的run()方法可能还没开始跑,甚至还在排队,getResult()自然拿不到任何有效结果。 - 更关键的是:你的
finally块属于主线程的for循环迭代逻辑,和Worker线程的生命周期半毛钱关系都没有,它根本不会等Worker跑完再执行。
下面给你两种靠谱的解决方案,优先推荐第一种,更符合Java多线程的规范用法。
方案一:改用Callable+Future获取异步结果
如果你能修改LittleDwarfWorker的代码,把它从Runnable改成Callable是最优解——因为Callable允许你返回任务执行结果,而ExecutorService.submit()会返回一个Future对象,帮你跟踪任务状态、获取结果。
第一步:修改LittleDwarfWorker实现Callable
把run()换成call()方法,直接返回你的数据库查询结果(把YourResultType换成你实际返回的类型,比如String、自定义POJO等):
public class LittleDwarfWorker implements Callable<YourResultType> { private DataSource dataSource; // 其他需要的参数 // 构造器,传入dataSource和其他参数 public LittleDwarfWorker(DataSource dataSource, ...) { this.dataSource = dataSource; // 初始化其他参数 } @Override public YourResultType call() throws Exception { JdbcTemplate localJdbcTemplate = new JdbcTemplate(this.dataSource); // 执行数据库查询,拿到结果 YourResultType result = ...; // 这里写你的查询逻辑 return result; } }
第二步:修改主线程代码,用submit()收集Future并获取结果
先把所有任务的Future收集起来,等所有任务都提交后,再逐个获取结果:
List<YourResultType> array = new ArrayList<>(); int i = 0; ExecutorService pool = Executors.newFixedThreadPool(50); // 用来保存所有任务的Future对象 List<Future<YourResultType>> futures = new ArrayList<>(); for(String str : strList) { LittleDwarfWorker littleDwarfWorker = new LittleDwarfWorker(dataSource, ...); try { // 用submit提交任务,拿到Future Future<YourResultType> future = pool.submit(littleDwarfWorker); futures.add(future); } catch(Exception e) { e.printStackTrace(); } finally { i++; if((i % 100) == 0) { log.info("Progression :" + i + "/" + strList.size()); } } } // 等待所有任务完成,收集结果 for(Future<YourResultType> future : futures) { try { // get()方法会阻塞主线程,直到对应任务完成并返回结果 YourResultType result = future.get(); if(result != null) { array.add(result); } } catch(InterruptedException | ExecutionException e) { // 捕获任务执行中的异常 e.printStackTrace(); } } // 关闭线程池 pool.shutdown(); // 可选:等待线程池完全关闭,避免程序提前退出 if(!pool.awaitTermination(60, TimeUnit.SECONDS)) { pool.shutdownNow(); }
这种方式的好处:
- 能精准控制结果的获取时机,确保拿到的是Worker执行完成后的有效结果。
- 可以通过
Future捕获任务执行过程中抛出的异常,排查问题更方便。 - 代码逻辑清晰,符合Java并发编程的最佳实践。
方案二:如果不能修改Worker的run()方法,让Worker主动添加结果
如果因为历史代码依赖,不能把Runnable改成Callable,那可以让Worker在任务完成后,主动把结果加到集合里——但一定要注意集合的线程安全!
第一步:用线程安全的集合
因为多个Worker线程会同时修改集合,普通的ArrayList会有并发问题,所以要用Collections.synchronizedList或者CopyOnWriteArrayList:
// 用线程安全的ArrayList List<Object> array = Collections.synchronizedList(new ArrayList<>()); int i = 0; ExecutorService pool = Executors.newFixedThreadPool(50);
第二步:修改Worker的构造器和run()方法
把线程安全的集合传给Worker,让它在任务完成后主动添加结果:
public class LittleDwarfWorker implements Runnable { private DataSource dataSource; private List<Object> resultList; // 构造器传入dataSource和结果集合 public LittleDwarfWorker(DataSource dataSource, List<Object> resultList) { this.dataSource = dataSource; this.resultList = resultList; } @Override public void run() { JdbcTemplate localJdbcTemplate = new JdbcTemplate(this.dataSource); // 执行数据库查询,拿到结果 Object result = ...; // 任务完成后,把结果加入线程安全的集合 resultList.add(result); } }
第三步:主线程等待所有任务完成
主线程提交完所有任务后,必须等待线程池里的所有任务都执行完,才能去使用array:
for(String str : strList) { LittleDwarfWorker littleDwarfWorker = new LittleDwarfWorker(dataSource, array); try { pool.execute(littleDwarfWorker); } catch(Exception e) { e.printStackTrace(); } finally { i++; if((i % 100) == 0) { log.info("Progression :" + i + "/" + strList.size()); } } } // 关闭线程池并等待所有任务完成 pool.shutdown(); if(!pool.awaitTermination(60, TimeUnit.SECONDS)) { pool.shutdownNow(); } // 现在array里就有所有Worker的结果了
这种方式的注意点:
- 必须使用线程安全的集合,否则会出现数据丢失、数组越界等并发异常。
- 主线程一定要等待线程池任务全部完成,否则
array里的结果可能不全。
内容的提问来源于stack exchange,提问作者Azrael_404
相关产品推荐
相关产品推荐

