zoukankan      html  css  js  c++  java
  • Java定时线程池停止超时任务

    一、背景
    题主最近遇到一个问题,本来通过ScheduledExecutorService线程池定时调度一个任务。奈何不知道为啥跑了2个多月,其中一个任务Hang住了,原本定时的任务则出现了问题。

    关于定时线程池,好多人认为设置好频率(比如1Min),它会按照这个间隔按部就班的工作。但是,如果其中一次调度任务卡住的话,不仅这次调度失败,而且整个线程池也会停在这次调度上。

    我们先从一个例子试着复现下问题:

    public class pool {
    private static class Runner implements Runnable {
    @Override
    public void run() {
    try {
    Thread.sleep(10000);
    System.out.println(new Date());
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    }
    
    public static void main(String[] args) {
    ScheduledExecutorService service 
    = Executors.newScheduledThreadPool(1);
    service.scheduleAtFixedRate(
    new Runner(), 0, 1, TimeUnit.SECONDS);
    }
    }
    

      


    先从Main看,启动一个定时线程池,每隔1S调度一次Runner。看上去,应该是1S调度一次,但是Runner的实际执行时间为10S,那多久会调度一次?答案是10S。
    所以说,这个Runner不管什么原因挂掉了或者Hang住了,那这个定时调度线程池基本就废了。

    二、解决方法
    那我们应该怎么解决这个问题?如果说定时线程池有任务调度的超时策略就完美了,很可惜并没有。
    我们想下在并发编程中,哪种方式有超时策略?
    对,Future有,那我们可以结合Future,提供一种自动停止超时任务的方式,来解决某个任务Hang住的问题。

    我们简单修改下,把sleep逻辑移动到Callable中,并在Runner中使用Future来控制超时。

    public class pool {
    private static class Caller implements Callable<Boolean> {
    @Override
    public Boolean call() {
    try {
    Thread.sleep(10000);
    System.out.println(new Date());
    return true;
    } catch (Exception e) {
    e.printStackTrace();
    }
    return false;
    }
    }
    
    private static class Runner implements Runnable {
    @Override
    public void run() {
    ExecutorService excutor = Executors.newSingleThreadExecutor();
    Future<Boolean> future = excutor.submit(new Caller());
    try {
    future.get(1, TimeUnit.SECONDS);
    } catch (TimeoutException e) {
    System.out.println("timeout");
    } catch (Exception e) {
    e.printStackTrace();
    } finally {
    excutor.shutdownNow(); // 强制终止任务
    }
    }
    }
    
    public static void main(String[] args) {
    ScheduledExecutorService service
    = Executors.newScheduledThreadPool(1);
    service.scheduleAtFixedRate(
    new Runner(), 0, 1, TimeUnit.SECONDS);
    }
    }
    

      

    备注:
    - 实现逻辑相当于转移了,把本来应该调度的任务交给了另外一个Future单线程去执行。因为存在超时逻辑,不会影响原有定时线程池的执行。
    - finally是否需要杀死线程池,因人而异。如果不杀死的话,那超时的任务会继续执行。

    题外话:如果你有好的解决方式,欢迎和题主探讨。谢谢。

  • 相关阅读:
    深度学习模型训练之偏差与方差
    介绍一个快速确定神经网络模型中各层矩阵维度的方法
    TensorFlow简要教程及线性回归算法示例
    Web项目开发中常见安全问题及防范
    时序数据库及应用场景简介
    互联网产品怎么做数据埋点
    简述分布式跟踪系统实现原理
    MFC- socket 编程
    win32 socket 编程(六)——UDP
    win32 socket编程(五)——客户端实例(TCP)
  • 原文地址:https://www.cnblogs.com/yuyu666/p/10069747.html
Copyright © 2011-2022 走看看