Java线程池的正确关闭方法,awaitTermination还不够

mac2022-06-30  82

问题说明

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

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

不过,这次遇到的问题是,子线程用到BufferedReader,而BufferedReader 的readLine是阻塞的,如果流没有关闭那么他一定会一直读取。 即便是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周年。

转载于:https://www.cnblogs.com/slankka/p/11609615.html

最新回复(0)