问题说明

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

一直以来,我坚信用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. APP自動化測試腳本3

    package com.lemon.day01; import java.net.MalformedURLException; import java.net.URL; import java.uti ...

  2. 链表-LinkList

    什么是链表 维基百科:链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer).由于不必须按顺序存 ...

  3. redis常用操作-键的生存时间

    System.out.println("设置 key001的过期时间为5秒:"+jedis.expire("key001", 5)); System.out.p ...

  4. GetThreadTimes获取其它线程cpu时间

    http://www.cnblogs.com/eaglet/archive/2009/03/11/1408809.html 鄙视下上面的垃圾博文,纯粹忽悠人 参考文章: http://blog.kal ...

  5. echarts使用——柱状图

    开发中,做报表统计的时候,很容易用到echarts实现折线图.饼状图.柱状图的绘制,使用echarts插件很简单,官网有教程实例,但主要是这些图需要的数据格式的转换. 我的柱状图实现效果: 第一部分 ...

  6. Android开发——Toolbar常用设置

    本篇笔记用来记录常用的Toolbar设置,如Toolbar颜色设置,显示返回按钮,显示右边三个点按钮 之前Android 使用的ActionBar,Android5.0开始,谷歌官方推荐使用Toolb ...

  7. latex转word公式 java (latextoword,latex_word,latex2word,latex_omml)

    latex_word 主要目的:     给大家分享一个我的原创作品:latex转为word公式(omml)工具 [java] 此工具主要用于将含有latex公式的文本下载成word时,将latex转 ...

  8. vue.js 实战 todo list

    vue.js 起源 vue.js 的作者是尤雨溪,是一名中国人,之前在谷歌工作,现在在全职维护 vue 项目. vue.js 是 2014 年推出来的.现在已经更新到 2.x 版本,3.0 版本会在 ...

  9. 294 div2 C. A and B and Team Training

    C. A and B and Team Training 题目:A and B are preparing themselves for programming contests. An import ...

  10. 关于MySQL的经典例题50道

    --1.学生表Student(S,Sname,Sage,Ssex) --S 学生编号,Sname 学生姓名,Sage 出生年月,Ssex 学生性别--2.课程表 Course(C,Cname,T) - ...