Java中的任务超时处理

1|0Java中的任务超时处理

1|1综述

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

1|2实现方式

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

1|0利用循环计算

利用循环检查时间来处理超时是最直观的思路, 但是这个做法如今几乎没什么人在使用了. 原因有两点, 首先虽然这个做法可以不使用线程/线程池, 但是"耗时操作通常不建议放在主线程中"这一点无论是后端项目还是在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来判断是否超时并跳出. 这里额外再提一句, 示例就不给出了.

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

1|0利用 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(); } } }

1|3总结

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


__EOF__

本文作者地维藏光
本文链接https://www.cnblogs.com/dwcg/p/18316517.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   地维藏光  阅读(212)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
点击右上角即可分享
微信分享提示