问题说明

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

一直以来,我坚信用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 boot缓存初体验

    spring boot缓存初体验 1.项目搭建 使用MySQL作为数据库,spring boot集成mybatis来操作数据库,所以在使用springboot的cache组件时,需要先搭建一个简单的s ...

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

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

  3. Servlet与Tomcat运行示例

    Servlet与Tomcat运行示例 本文将写一个servlet,然后将其部署到Tomcat的全过程.本文参考<深入拆解Tomcat_Jetty>内容. 一.基于web.xml开发步骤 下 ...

  4. phpStudy后门漏洞利用复现

    phpStudy后门漏洞利用复现 一.漏洞描述 Phpstudy软件是国内的一款免费的PHP调试环境的程序集成包,通过集成Apache.PHP.MySQL.phpMyAdmin.ZendOptimiz ...

  5. Web前端开发的应用和前景——web 1.0到web 3.0

    Web前端开发的应用和前景--web 1.0到web 3.0 Web1.0:(只读时代) 以静态.单向阅读为主,网站内信息可以直接和其他网站信息进行交互,能通过第三方信息平台同时对多家网站信息进行整合 ...

  6. Linux被中断的系统调用

    慢系统调用,指的是可能永远无法返回,从而使进程永远阻塞的系统调用,比如无客户连接时的accept.无输入时的read都属于慢速系统调用. 在Linux中,当阻塞于某个慢系统调用的进程捕获一个信号,则该 ...

  7. docker升级后启动报错400 Client Error: Bad Request ("Unknown runtime specified docker-runc")

    宝塔面板docker升级后启动容器时报错400 Client Error: Bad Request ("Unknown runtime specified docker-runc" ...

  8. SparkStreaming整合Flume的pull报错解决方案

    先说下版本情况: Spark 2.4.3 Scala 2.11.12 Flume-1.6.0 Flume配置文件: simple-agent.sources = netcat-source simpl ...

  9. jQuery.noConflict()解决imgBox.js依赖jquery版本问题

    jQuery提供两种点击图片放大效果出处 在使用imgbox.js是出现的jquery版本不兼容问题,之后了解到jQuery.noConflict()的用法 jQuery.noConflict()的存 ...

  10. nextjs:如何将静态资源发布到 CDN

    nextjs 是基于 react 的服务端同构指出框架,在使用的过程中也多多少少遇到过几个问题,其中最大的问题就是静态资源的发布了. 1. 如何基于文件内容进行 hash 命名 Next.js use ...