问题说明

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

一直以来,我坚信用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. 黑苹果之DELL台式机安装Mac OS X 10.13.6版本操作系统

    由于本人所在的公司一般都是DELL的品牌台式机,所以以DELL台式机做小白鼠.记得在2012年的时候,在当时的那家公司为了学习自学IOS开发的Objective-C开发语言的时候,由于囊中羞涩买不起m ...

  2. java Swing 界面化查询数据库表

    两天从0基础写的.没有按钮对话框功能,只是简单的实现. 当然代码上有很多需要优化的,基本需要重写哈哈哈.但是我怕以后有需要所以还是存一下好了.<把RS结果集,放vector里面,用vector构 ...

  3. Redis 相关功能和实用命令(五)

    慢查询原因分析 由于 Redis 是单线程的,它内部维护了一个命令队列,所以当有耗时的命令出现时,比如 keys *,后面的命令会被阻塞,通查查出慢查询可以对服务进一步优化. 设置慢查询阀值:默认10 ...

  4. Elasticsearch实战-磁盘IO被打满

    背景 事情是这样的.一天下午4点42分左右.业务反馈我开发的服务在测试环境出现问题,返回资源数据是0.查日志发现是ES访问超时.相当于数据库挂了.持续了20多分钟自己恢复.咨询了ES团队,最终得到下面 ...

  5. chown、chgrp、chmod

    1.权限对应的数值 一开始理解权限对应的数值总是要去用二进制去算例如r-x是多少rwx是多少,后来才知道r就 是4,w就是2,x就是1,不管权限怎么变,他们对应的数值就是对应位相加.. 权限对于文件来 ...

  6. laravel基础操作手册

    laravel基础操作手册 1.路由配置 测试配置路由: Route::get('/test', 'TestController@index'); 2.控制器书写 3.模型文件 4.增加扩展类文件 L ...

  7. 使用scm-manager搭建git/svn 代码管理仓库

    使用 scm-manager 搭建 git/svn 代码管理仓库 1.在官网上下载scm-manager 下载地址https://www.scm-manager.org/download/ 2. 配置 ...

  8. mapper插入时显示中文

    可能是jdbc url需要加characterEncoding=utf-8,例 jdbc:mysql://localhost:3306/smbms?characterEncoding=utf8

  9. 网络下载器 Internet Download Manager v6.35.5 绿色便携版

    Internet Download Manager,简称 IDM,是国外的一款优秀网络下载工具.目前凭借着下载计算的速度优势在外媒网站中均受好评,现在已被多数国人熟知.Internet Downloa ...

  10. 信息传递 NOIP2015 day1 T2

    题文: 有n个同学(编号为1到n)正在玩一个信息传递的游戏.在游戏里每人都有一个固定的信息传递对象,其中,编号为i的同学的信息传递对象是编号为Ti同学. 游戏开始时,每人都只知道自己的生日.之后每一轮 ...