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


    问题说明

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

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

  • 相关阅读:
    【代码笔记】Web-ionic-toggle(切换开关)
    【代码笔记】Web-ionic-表单和输入框
    【代码笔记】Web-ionic-卡片
    【代码笔记】Web-ionic-列表
    【代码笔记】Web-ionic-按钮
    【代码笔记】Web-ionic-头部与底部
    【代码笔记】Web-ionic-index创建侧边栏
    【代码笔记】Web-ionic-创建APP的架构
    【工具相关】Web-ionic-ionicLab的使用
    【转】]Android实现开机自动运行程序
  • 原文地址:https://www.cnblogs.com/slankka/p/11609615.html
Copyright © 2020-2023  润新知