问题说明

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

一直以来,我坚信用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周年。

Java线程池的正确关闭方法,awaitTermination还不够的更多相关文章

  1. JUC学习笔记--从阿里Java开发手册学习线程池的正确创建方法

    前言 最近看阿里的 Java开发手册,上面有线程池的一个建议: [强制]线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式, 这样的处理方式让写的同学 ...

  2. 从阿里Java开发手册学习线程池的正确创建方法

    前言 最近看阿里的 Java开发手册,上面有线程池的一个建议: [强制]线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更 ...

  3. 【转】线程池体系介绍及从阿里Java开发手册学习线程池的正确创建方法

    jdk1.7中java.util.concurrent.Executor线程池体系介绍 java.util.concurrent.Executor : 负责线程的使用与调度的根接口  |–Execut ...

  4. Java线程池基础

    目录: 一.线程池概述 二.线程池参数 三.线程池的执行过程 四.线程池的主要实现 五.线程池的使用 六.线程池的正确关闭方式 七.线程池参数调优 一.线程池概述 1.线程池类 目前线程池类一般有两个 ...

  5. 如何优雅的关闭Java线程池

    面试中经常会问到,创建一个线程池需要哪些参数啊,线程池的工作原理啊,却很少会问到线程池如何安全关闭的. 也正是因为大家不是很关注这块,即便是工作三四年的人,也会有因为线程池关闭不合理,导致应用无法正常 ...

  6. 使用Java 线程池的利弊及JDK自带六种创建线程池的方法

    1. 为什么使用线程池 诸如 Web 服务器.数据库服务器.文件服务器或邮件服务器之类的许多服务器应用程序都面向处理来自某些远程来源的大量短小的任务.请求以某种方式到达服务器,这种方式可能是通过网络协 ...

  7. 并发编程(十二)—— Java 线程池 实现原理与源码深度解析 之 submit 方法 (二)

    在上一篇<并发编程(十一)—— Java 线程池 实现原理与源码深度解析(一)>中提到了线程池ThreadPoolExecutor的原理以及它的execute方法.这篇文章是接着上一篇文章 ...

  8. Java线程状态、线程start方法源码、多线程、Java线程池、如何停止一个线程

    下面将依次介绍: 1. 线程状态.Java线程状态和线程池状态 2. start方法源码 3. 什么是线程池? 4. 线程池的工作原理和使用线程池的好处 5. ThreadPoolExecutor中的 ...

  9. Netty核心概念(7)之Java线程池

    1.前言 本章本来要讲解Netty的线程模型的,但是由于其是基于Java线程池设计而封装的,所以我们先详细学习一下Java中的线程池的设计.之前也说过Netty5被放弃的原因之一就是forkjoin结 ...

随机推荐

  1. Spring 梳理 - WebMvcConfigurerAdapter详解

    参考:https://blog.csdn.net/weixin_43453386/article/details/83623242

  2. 阿里云服务器CentOS6.9安装maven

    1.下载maven http://maven.apache.org/download.cgi 2.移动到linux yangyuke用户下(此处由于我设置进入linux的是自定义用户yangyuke, ...

  3. JS调用activeX实现浏览本地文件夹功能 wekit内核只需要<input type="file" id="files" name="files[]" webkitdirectory/>即可,IE内核比较麻烦

    研究了一天,js访问本地文件本身是不可能的,只能借助于插件.植入正题,IE仅支持ActiveX插件. function openDialog() { try { var Message = " ...

  4. win10 cnpm安装完之后一直说不是内部命令的原因

    找到cnpm的默认安装路径 一般默认的是 D:\Program Files\nodejs\node_modules 然后添加环境变量中 win10是在系统环境变量中切记不是在用户变量中.保存之后,重新 ...

  5. linux系统下使用xampp 丢失mysql root密码 只能远程访问,本地无法连接数据库

    如果在ubuntu 下面 使用xampp这个集成开发环境,却忘记mysql密码. 当出现只能远程访问的,本地无法访问,通常是host改成% 远程访问,本地访问到一个是空壳.这是权限的问题 需要修hos ...

  6. Openshift yum安装

    Openshift yum安装: Yum 安装docker [root@DockerServer openshift]# yum repolist [root@DockerServer openshi ...

  7. FFmpeg(六) 播放视频之GLSurfaceView显示RGB数据

    一.播放视频说明 1.两种方式播放视频 ①shader播放YUV,后面再介绍. ②RGB直接显示数据,简单.性能差,用到FFmpeg的格式转换,没有shader效率高.本文介绍这个方式. 2.GLSu ...

  8. 【Java】支付宝获取人脸采集认证的图片base64格式

    人脸识别结果查询接口zoloz.identification.user.web.query返回的imgStr图片字符串并不是标准的base64格式,解析不出图片. 由于标准的Base64并不适合直接放 ...

  9. ElasticSearch Cardinality Aggregation聚合计算的误差

    使用ES不久,今天发现生产环境数据异常,其使用的ES版本是2.1.2,其它版本也类似.通过使用ES的HTTP API进行查询,发现得到的数据跟javaClient API 查询得到的数据不一致,于是对 ...

  10. 索引的底层实现(B 树)

    一.B 树 1.B-Tree介绍 B-树的搜索,从根结点开始,对结点内的关键字(有序)序列进行二分查找,如果命中则结束,否则进入查询关键字所属范围的儿子结点:重复,直到所对应的儿子指针为空,或已经是叶 ...