首页 > 编程语言 >Java中的任务超时处理

Java中的任务超时处理

时间:2024-10-07 18:33:22浏览次数:8  
标签:Java TimeoutTask private System 任务 static new 超时

Java中的任务超时处理

综述

任务超时处理是编程世界中一个不可或缺且常见的处理机制, 特别是在进行耗时任务时, 如网络请求, 数据库查询, 大规模数据处理等场景。在这些情况下, 为了防止任务因各种不可预测的因素(如网络延迟, 服务器响应超时, 数据量过大等)而无休止地占用宝贵的系统资源, 开发者通常会在任务执行逻辑中引入超时机制.

实现方式

在Java中,超时任务的处理主要通过异常处理、并发控制、线程管理等方式实现。例如,使用java.util.concurrent包中的Future接口和ExecutorService工具类,结合Callable接口和FutureTask,可以方便地设置超时任务。开发人员可以通过设置任务执行的超时时间,一旦任务执行超过设定的时间,系统将抛出TimeoutException,通知开发者任务超时,从而及时采取相应的补救措施。

利用循环计算

利用循环检查时间来处理超时是最直观的思路, 但是这个做法如今几乎没什么人在使用了. 原因有两点, 首先虽然这个做法可以不使用线程/线程池, 但是"耗时操作通常不建议放在主线程中"这一点无论是后端项目还是在android项目中都基本属于共识范畴, 所以仍然需要使用线程/线程池. 其次, 循环检查无法处理因阻塞引起的超时, 这意味着直到线程从阻塞恢复才能执行判断是否退出的语句.

所以无论从性能还是安全性来判断使用 ExecutorService 提供的 api 来实现才是更好的选择. 此处给出示例仅供了解.

public class TimeoutTask {
    private ExecutorService executorService;

    private TimeoutTask() {
        this.executorService = new ThreadPoolExecutor(
                2,
                Runtime.getRuntime().availableProcessors() * 2,
                10, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(100),
                new ThreadPoolExecutor.CallerRunsPolicy()
        );
    }

    private static class SingletonHolder {
        private static final TimeoutTask instance = new TimeoutTask();
    }

    //单例模式
    public static TimeoutTask getInstance() {
        return SingletonHolder.instance;
    }

    private static String formatTime(long time) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        return sdf.format(new Date(time));
    }

    public void timeoutWithLoop(long millisecondLimit) {
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                long startTime = System.currentTimeMillis();

                for (; ; ) {
                    var currentTime = System.currentTimeMillis();
                    System.out.println("当前的时间: " + formatTime(currentTime));
                    //这里使用thread.sleep模拟耗时操作
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    if (currentTime - startTime > millisecondLimit) {
                        //为了方便测试代码运行结束自动关闭额外使用了shutdown
                        executorService.shutdown();
                        break;
                    }
                }
            }
        });
    }
}

public class Main {
    public static void main(String[] args) {

        TimeoutTask.getInstance().timeoutWithLoop(4000);
    }
}

另外, 通过循环处理超时任务还有一个变种做法, 那就是通过 Timer 或者其他延时api控制某个 AtomicBoolean 格式的flag进行转变, 在循环中通过检查flag来判断是否超时并跳出. 这里额外再提一句, 示例就不给出了.

当然, 这个变种做法依然存在此前提到的两种问题, 同样不推荐, 仅供了解.

利用 future.get() 和 future.cancel() 方法

这应该是当前处理异步任务超时情况下最好的处理方式, 通过 executorService.submit() 获取 future , 通过 future.get() 设置超时期限, 再在 TimeoutException 触发时通过 future.cancel() 取消超时任务.

public class TimeoutTask {
    private ExecutorService executorService;

    private TimeoutTask() {
        this.executorService = new ThreadPoolExecutor(
                2,
                Runtime.getRuntime().availableProcessors() * 2,
                10, 
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(100),
                new ThreadPoolExecutor.CallerRunsPolicy()
        );
    }

    private static class SingletonHolder {
        private static final TimeoutTask instance = new TimeoutTask();
    }

    //单例模式
    public static TimeoutTask getInstance() {
        return SingletonHolder.instance;
    }

    private static String formatTime(long time) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        return sdf.format(new Date(time));
    }

    public void timeoutWithFuture(long targetTime) {
        Future<?> future = executorService.submit(() -> {
            try {
                TimeUnit.MILLISECONDS.sleep(8000);
                var awaitEndTime = System.currentTimeMillis();
                System.out.println("通过condition.await()等待结束时间: "+ formatTime(awaitEndTime) + "这一行不应该被打印");
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });

        try {
            var startTime = System.currentTimeMillis();
            System.out.println("超时示例开始时间: "+ formatTime(startTime));

            future.get(targetTime, TimeUnit.MILLISECONDS);
        } catch (TimeoutException e) {
            // 超时处理
            var endTime = System.currentTimeMillis();
            System.out.println("任务按照预想超时结束, 结束时间: "+ formatTime(endTime));
            future.cancel(true);
        } catch (ExecutionException | InterruptedException e) {
            System.out.println("报错信息: "+e.getMessage());
        } finally {
            executorService.shutdown();
        }
    }
}

总结

综上所述, Java中的任务超时处理是一个非常重要且常见的编程实践. 处理这个问题时, 利用Java并发包中的Future接口和ExecutorService工具类基本可以被看作常规场景下的最优选. 这一做法简单有效. 另外, 在中型以上的项目中还可以进一步拓展, 将超时处理封装成类, 通过自定义的方法, 类似 executeWithTimeout(callable timeOutCallback, long timeInMillisecond, runnable onTimeOut){} 这样的封装. 这样可以不需要每次使用时都写一大堆套路代码, 减小工作量.

标签:Java,TimeoutTask,private,System,任务,static,new,超时
From: https://www.cnblogs.com/dwcg/p/18316517

相关文章

  • Java中的策略模式
    Java中的策略模式综述本文总结了策略模式的定义,特点,使用场景以及实现思路。策略模式的定义策略模式说通了,就是定义一系列的算法,将它们各自封装起来,并且使用一个共同的接口使它们可相互替换.使得算法和算法之间没有耦合,这样如果方法需要修改或者添加,工程师不需要修......
  • Java基础第八章(多态)
    多态1.方法体现多态方法重载体现多态对象通过传入不同数量的参数,会调用不同的sun方法,体现出多态方法重写体现多态A类和B类是继承关系,通过不同对象调用对应类中重写的方法体现2.对象体现多态编译是javac,运行是java(1)一个对象的编译类型和运行类型可以不一致将父......
  • JavaScript 小知识:轻松搞定 ArrayBuffer 到 Base64 的转换
    关键词:ArrayBuffer,Base64,栈溢出,TextDecoder,btoa,性能优化,JavaScript,兼容性摘要本文探讨了在JavaScript中将ArrayBuffer转换为Base64字符串时遇到的栈溢出问题,并提供了几种实用的解决方案。我们将通过生动的比喻来解释相关概念,比较不同方法的性能和兼......
  • JavaScript 小知识:轻松搞定 ArrayBuffer 到 Base64 的转换
    关键词:ArrayBuffer,Base64,栈溢出,TextDecoder,btoa,性能优化,JavaScript,兼容性摘要本文探讨了在JavaScript中将ArrayBuffer转换为Base64字符串时遇到的栈溢出问题,并提供了几种实用的解决方案。我们将通过生动的比喻来解释相关概念,比较不同方法的性能和兼......
  • JavaScript 小知识:轻松搞定 ArrayBuffer 到 Base64 的转换
    关键词:ArrayBuffer,Base64,栈溢出,TextDecoder,btoa,性能优化,JavaScript,兼容性摘要本文探讨了在JavaScript中将ArrayBuffer转换为Base64字符串时遇到的栈溢出问题,并提供了几种实用的解决方案。我们将通过生动的比喻来解释相关概念,比较不同方法的性能和兼......
  • 基于JAVA的鲜花商城管理系统(源码+定制+讲解)鲜花商城管理系统、鲜花商城管理平台、鲜
    博主介绍:  ✌我是阿龙,一名专注于Java技术领域的程序员,全网拥有10W+粉丝。作为CSDN特邀作者、博客专家、新星计划导师,我在计算机毕业设计开发方面积累了丰富的经验。同时,我也是掘金、华为云、阿里云、InfoQ等平台的优质作者。通过长期分享和实战指导,我致力于帮助更多学生......
  • Java与线程
    Java与线程1.线程的实现线程是比进程更轻量级的调度执行单位,线程的引人,可以把一个进程的资源分配和执行调度分开,各个线程既可以共享进程资源(内存地址、文件IO等),又可以独立调度。目前线程是Java里面进行处理器资源调度的最基本单位。主流的操作系统都提供了线程实现,Java语言......
  • java_day9_包的划分、形式参数、返回值、修饰符、内部类、匿名内部类
    一、包包的划分好处:1、方便管理我们的代码文件2、不同包下的文件名可以是一样的常见的包命名规则:1、按照功能划分增加-学生增加-老师增加删除-学生删除-老师删除修改......
  • 基于java+springboot的医院预约挂号系统小程序(源码+lw+部署文档+讲解等)
    课题简介医院预约挂号系统基于Java和SpringBoot开发,是改善医疗服务流程、提高患者就医体验的重要工具。该系统利用Java的稳定性和强大性能,以及SpringBoot的便捷开发框架,确保系统可靠运行和易于维护。它包含了患者信息管理、医生信息管理、科室信息管理、预约管......
  • 基于java+springboot的医学电子技术线上翻转课堂系统(源码+lw+部署文档+讲解等)
    课题简介医学电子技术线上翻转课堂系统基于Java和SpringBoot开发,是为医学教育领域量身打造的创新教学平台。该系统借助Java的稳定性和强大性能,以及SpringBoot的高效开发特性,确保系统能够稳定运行且易于维护和扩展。它涵盖了丰富的功能模块,包括课程资源管理、学......