问题说明

今天发现了一个问题,颠覆了我之前对关闭线程池的认识。

一直以来,我坚信用shutdown + awaitTermination关闭线程池是最标准的方式。

不过,这次遇到的问题是,子线程用到BufferedReader,而BufferedReaderreadLine是阻塞的,如果流没有关闭那么他一定会一直读取。

即便是awaitTermination执行完,超时之后返回到主线程。但是子线程没有像预计的那样中断退出,awaitTermination 是不会中断线程的。

BufferedReader reader = ....
String buf;
while ((buf = reader.readLine()) != null) {
buffer.appendBuffer(buf);
}
public static <T> void executeCommand(Callable<T> callable) {
BasicThreadFactory build = new BasicThreadFactory.Builder()
.daemon(false)
.namingPattern("exec-comA")
.build();
ExecutorService executorService = Executors.newSingleThreadExecutor(build);
Future<T> submit = executorService.submit(callable);
executorService.shutdown();
try {
if(!executorService.awaitTermination(60, TimeUnit.SECONDS)){
// 超时的时候向线程池中所有的线程发出中断(interrupted)。
// executorService.shutdownNow();
}
System.out.println("AwaitTermination Finished");
} catch (InterruptedException ignore) {
// executorService.shutdownNow();
}
}

jstack如下:

"exec-comA" #12 prio=5 os_prio=0 tid=0x0000000020f86800 nid=0x419c in Object.wait() [0x0000000021ece000]
java.lang.Thread.State: TIMED_WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076f272cb0> (a com.jcraft.jsch.Channel$MyPipedInputStream)
at java.io.PipedInputStream.read(PipedInputStream.java:326)
- locked <0x000000076f272cb0> (a com.jcraft.jsch.Channel$MyPipedInputStream)
at java.io.PipedInputStream.read(PipedInputStream.java:377)
- locked <0x000000076f272cb0> (a com.jcraft.jsch.Channel$MyPipedInputStream)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
- locked <0x000000076f2837d0> (a java.io.InputStreamReader)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.BufferedReader.fill(BufferedReader.java:161)
at java.io.BufferedReader.readLine(BufferedReader.java:324)

这里可以跟进代码,查看PipedInputStream的读方法,一定是一直在循环中等待数据的 while(in < 0)

结论

shutdown + awaitTermination关闭线程池是最标准的方式。这话不错,但是这样不能确保子线程按照预想的那样退出。

因此还需要 executorService.shutdownNow();来主动中断所有子线程。

方法二

import org.apache.commons.exec.Watchdog;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import java.io.*; //.................................................................... Watchdog watchdog = new Watchdog(30000); Thread thread = Thread.currentThread(); watchdog.addTimeoutObserver(w -> thread.interrupt()); watchdog.start();
try{ //耗时操作 watchdog.stop(); } catch (Exception e) {
e.printStackTrace();
} finally{
//clean some resources
watchdog.stop();
}

这种方式可以使得开发者更加明确的知道,这个耗时任务,超时就要退出终止的。

这样这个世界就会少很多转圈圈。

最后这里是2019年国庆节前最后一篇博客,

恭祝2019年祖国成立70周年。

最新文章

  1. CSS 3 颜色表示法
  2. RAID与双机热备简单介绍与区别
  3. 查看进程的io
  4. rsync 安装与配置
  5. 基于curl 的zabbix API调用
  6. c++基础五个题(二)
  7. kairosdb + cassandra Setup
  8. 一个简单的freemark输入输出的案例(一)
  9. 浅论各种调试接口(SWD、JTAG、Jlink、Ulink、STlink)的区别
  10. 磊哥测评之数据库SaaS篇:腾讯云控制台、DMC和小程序
  11. 递归打印lua中的table
  12. 2个简单实例让你快速理解try-catch的用法
  13. js计算器---转
  14. jquery接触初级-----ajax 之:load()方法
  15. Spring 学习02
  16. Windows服务的安装和卸载
  17. SQL学习笔记一之初识数据库
  18. idea tomcat添加
  19. 【题解】SCOI2008配对
  20. AOJ.720 丢失的学妹

热门文章

  1. lcx端口转发
  2. Chrome 调试AJAX请求返回的JS脚本
  3. 基于C#的机器学习--c# .NET中直观的深度学习
  4. js 跳转链接的几种方式
  5. SpringBootSecurity学习(13)前后端分离版之JWT
  6. Kubernetes 系列(八):搭建EFK日志收集系统
  7. API文档注释 Javadoc
  8. 教你使用Cocos Creator制作国旗头像生成器,附源码!
  9. 通过搭建MySQL掌握k8s(Kubernetes)重要概念(下):参数配置
  10. 基于Docker和Golang搭建Web服务器