Java调用CompletionService.take()后程序挂起无法退出问题排查
问题根因
通过Executors.newCachedThreadPool()创建的线程池,默认内置线程工厂生成的是非守护类用户线程。JVM进程正常终止的判断标准是所有用户线程全部执行完毕,而示例代码在拿到异步任务返回结果后,未主动关闭持有的线程池:
ExecutorCompletionService只是异步任务结果的封装组件,实际持有工作线程资源的是构造时传入的线程池实例- CachedThreadPool默认给空闲线程设置了60秒的存活等待期,空闲阶段线程会一直阻塞等待新任务提交,不会主动销毁
- 只要存在存活的用户线程,JVM就不会退出,因此业务逻辑输出完成后程序会一直挂起。即便等待60秒等空闲线程被自动回收、进程退出,这种依赖线程自动超时回收的写法也不符合线程池使用规范,存在资源泄漏风险。
修改方案
在确认所有任务处理完成、不需要再提交新任务时,主动调用线程池的关闭方法释放线程资源即可。修改后可正常执行退出的代码如下:
public static void main(String[] args) { // 单独持有线程池实例,用于后续资源释放 ExecutorService threadPool = Executors.newCachedThreadPool(); CompletionService<Integer> cs = new ExecutorCompletionService<>(threadPool); cs.submit(new Callable<Integer>() { public Integer call(){ try{ Thread.sleep(3000); // 睡眠3秒后打印 System.out.println("Sleeping thread: " + Thread.currentThread().getId()); }catch(InterruptedException e){ e.printStackTrace(); } return 10; } }); try{ Future<Integer> fi = cs.take(); System.out.println(fi.get()); }catch(Exception e){ e.printStackTrace(); }finally { // 任务执行完成后平滑关闭线程池 threadPool.shutdown(); } }
补充说明:
shutdown()会执行平滑关闭逻辑:不再接收新提交的任务,等待池中已提交的所有任务执行完成后,销毁全部工作线程释放资源。如果需要立即中断所有运行中的任务终止线程池,可以使用shutdownNow()方法,日常开发无特殊需求优先使用平滑关闭方式。
内容的提问来源于stack exchange,提问作者Troskyvs
相关产品推荐
相关产品推荐

