您现在的位置是:首页 > 正文

Java任务超时处理机制实现

2024-02-01 05:22:33阅读 3

1问题描述

    在应用软件的开发中,经常会遇到这样的一种需求:需要实现一个方法来执行某种任务,而这个方法的执行时间不能超过指定值,如果超时,则调用者不管这个方法将来是否可能执行成功,都要中断它的执行,或者让这个方法返回。这就是超时处理问题。

    根据执行任务的方法是否异步,可以把问题从两个方面分析:如果方法顺序执行,则方法执行时整个程序的控制权在执行任务的方法中,方法调用者对于任务的超时无能为力,只能寄希望于执行任务的方法能够在任务的每轮循环中判断是否超时,以便随时自己返回;如果任务方法异步执行,即执行任务的方法是另一个线程,则可以通过主线程和任务线程的线程间协作来实现任务线程的超时中断处理。

2解决方案

    根据上面对问题的分析,可以提出三种解决方案,一种同步的解决方案和两种异步的解决方案。

2.1串行超时处理

    串行超时处理是指程序只有一个线程,调用者调用任务方法,完全由执行任务的方法本身进行超时处理。

    这种方案通常要求任务需要循环执行,每个循环内的计算较复杂,执行时间较长或者不确定。执行任务的方法在规划任务算法代码的同时还要考虑超时的时候能够退出。通常的代码框架如下:

1 public void runTask(long timeout){
2         long beginTime=System.currentTimeMillis();
3         //任务执行准备
4         //如下为任务算法执行
5         while((System.currentTimeMillis()-beginTime<timeout)&&(任务自身的逻辑判断)){
6               //执行循环体内的任务片段和算法
7         }
8 }

    通过这种方案实现的任务超时处理最大的优点是方案简单,因为不会引入新的线程,完全串行操作,但是这种方案也有两大缺点:

1、代码混乱,因为方法除了实现任务,还要考虑超时,违反了方法的职责单一原则

2、该方案无法处理因阻塞引起的超时情况

    第二个缺点是这个方案的最大限制。如果循环体内出现诸如IO阻塞而引起的程序执行挂起,比如socket.accept(),或者inputstream.read(),这样方法在因阻塞引起的超时发生后将不会返回,因为这时根本无法执行到下次循环的判断条件处。

    为了能够处理阻塞超时的情况,只能借助异步多线程方式来完成。

2.2利用wait/notify实现异步超时处理

    利用多线程机制实现任务方法的异步执行很简单,只需要创建一个类实现Runnable接口,把任务放在重写的run方法中,run方法即为处理任务的方法。在主线程中使用Thread类创建并启动新任务线程即可。

    但是,如果需要处理任务线程可能的超时情况,就需要wait/notify机制让主线程和任务线程同步了。具体思路是:让主线程在启动任务线程之后进行带超时参数的wait操作,如果任务线程超时,则wait不再等待,wait返回后主动中断任务线程;如果在超时时间内任务线程执行完毕,则通过notify方法通知主线程,这样主线程的wait方法也可以返回。

    主线程代码框架如下,假设任务线程为TaskThread:

01 public void caller(){
02         Object monitor=new Object();
03         TaskThread task=new TaskThread();
04         Thread thread=new Thread(task);
05         //对task对象进行各种set操作以初始化任务
06         try{
07         synchronized(monitor){
08             thread.start();
09         while(线程没有顺利完成){
10             monitor.wait(timeout);
11         }
12         //线程顺利结束,获取并处理结果
13         }
14         }
15         catch(InterruptException e){
16             //等待已经被超时或者其他原因中断,终止线程thread
17         }
18         finally{
19         //进行资源回收等收尾工作
20         }
21 }

    任务线程TaskThread通过run方法实现任务,代码框架如下:

01 @Override
02 public void run(){
03         //各种任务执行准备
04         while((任务没有被要求停止)&&(任务本身的各种判断条件)){
05             //本次循环中的子任务处理
06         //包括各种可能的IO阻塞和挂起等待操作
07         }
08         synchronized(monitor){  //此处的monitor引用必须主线程中的monitor对象
09         monitor.notify()    //任务执行完毕,唤醒主线程的wait操作
10         }
11 }

    这种方案可以处理因为IO或其他原因阻塞而引起任务执行超时的情况,当主线程因为wait超时抛出异常时,可以中断任务线程。但是需要注意的是,调用thread.stop()停止线程这种简单暴力的方法是不提倡使用的,而要用其他方法停止线程,而且停止一个处于阻塞状态的线程尤其复杂。

    这种方案也存在如下缺点:

1、使用线程间同步操作,增加代码复杂度

2、Runnable接口的run方法没用返回值,不允许抛出异常,不便于任务完后了结果的返回

3、终止未完成的任务线程操作复杂

2.3利用Callable接口实现异步超时处理

    为了去掉线程间的同步操作,以及能够让任务线程方法有返回值和抛出异常,可以使用Callable接口来替代Runnable接口,相应的主线程也需要相应变动。

    Callable接口位于java.utils.concurrent包中,其抽象方法call()有返回值,可以抛出异常。调用Callable接口需要ExecutorService接口实例,而获取call方法的返回值需要Future接口实例。

    主线程代码框架如下:

01 public void caller() throws InterruptedExceptio,TimeoutExceptio,ExecutorException{
02         TaskThread task=new TaskThread();   //实现Callable接口的任务线程类
03         ExecutorService exec=Executors.newFixedThreadPool(1);
04         //对task对象进行各种set操作以初始化任务
05         Future<String> future=exec.submit(task);
06         try{
07         return future.get(this.timeout, TimeUnit.MILLISECONDS);
08         }
09          finally{
10         if(任务线程没有顺利结束){
11             //终止线程task
12         }
13         exec.shutdownNow();
14         }
15 }

    TaskThread由实现Runnable接口变成了实现Callable接口,call方法的代码和run方法类似,只不过不需要notify方法的调用了。代码框架如下:

1 @Override
2 public String call() throws AnyException{
3         //各种任务执行准备
4         while((任务没有被要求停止)&&(任务本身的各种判断条件)){
5             //本次循环中的子任务处理
6         //包括各种可能的IO阻塞和挂起等待操作
7         }
8 }

    通过比较可以看出,这种方案的代码更为简洁方便,虽然当任务超时时,终止任务线程的方法依然复杂,但是相比于前一种更简洁,而且比第一种串行方案更具有通用性。

    需要指出的是,如何停止一个线程是一个比较复杂而有一定技巧的工作(千万别说用Thread stop方法),因此并没有在本文中论述具体方法,这方面的知识可以参考资料[1,2],也可以参考我写的专门论述如何停止线程的文章http://blog.csdn.net/mikeszhang/article/details/8751355

 

3参考资料

[1] Why Are Thread.stop, Thread.suspend, Thread.resume and Runtime.runFinalizersOnExit Deprecated?http://docs.oracle.com/javase/1.5.0/docs/guide/misc/threadPrimitiveDeprecation.html.

[2] How to Stop a Thread or a Task.http://forward.com.au/javaProgramming/HowToStopAThread.html.

[3] JavaTM Platform Standard Edition 6 API. http://www.ostools.net/apidocs/apidoc?api=jdk-zh.

[4] Bruce Eckel. Thinking in Java, 4th Edition. Prentice Hall, 2006.

 http://my.oschina.net/u/1010788/blog/119361

网站文章

  • 一个字节是几位?

    一个字节为8位。字节含义:字节是计算机信息技术用于计量存储容量的一种计量单位,也表示一些计算机编程语言中的数据类型和语言字符。数据存储是以“字节”(Byte)为单位,数据传输是以大多是以“位”(bit...

    2024-02-01 05:22:27
  • 【MATLAB】如何安装配置MinGW-w64 C/C++编译器 热门推荐

    【MATLAB】如何安装配置MinGW-w64 C/C++编译器 热门推荐

    【MATLAB】如何安装配置MinGW-w64 C/C++编译器 win10 + matlab2018b 具体方法: 1.打开你的matlab,选择“附加功能”,选择“获取附加功能” 2.在“附加功能...

    2024-02-01 05:22:00
  • 25.理一理关于tensorflow的各种骚操作

    1.tf.squeezehttps://www.jianshu.com/p/a21c0bc10a382.tf.casthttps://blog.csdn.net/dcrmg/article/details/797478143.tf.expand_dims https://www.cnblogs.com/helloworld0604/p/90017...

    2024-02-01 05:21:53
  • ‘mysql‘ 不是内部或外部命令,也不是可运行的程序或批处理文件终极大坑

    ‘mysql‘ 不是内部或外部命令,也不是可运行的程序或批处理文件终极大坑

    ‘mysql’ 不是内部或外部命令,也不是可运行的程序或批处理文件的解决方案‘mysql’ 不是内部或外部命令,也不是可运行的程序或批处理文件的解决方案,关于这个问题其实还是很好解决的,网上一搜全都有...

    2024-02-01 05:21:46
  • Python-Django篇----用Python搭建自己的服务(一)(2):配置文件介绍并运行本地服务

    Python-Django篇----用Python搭建自己的服务(一)(2):配置文件介绍并运行本地服务

    从这一篇文章开始呢,就涉及到代码的编写了,因为写的是Python嘛,所以建议大家去弄一个PyCharm,当然,也可以直接用命令行编写,我这里呢就用编译器方便介绍。 首先呢,我们看上篇文章中创建的项目文件, 主要呢我们就关心一下settings和urls文件就行了,wsgi就是个web服务的网端接口配置文件,我们不太care这东...

    2024-02-01 05:21:16
  • python 文件、文件夹详细操作

    python 文件、文件夹详细操作

    一、文件操作1、文件操作流程2、文件打开模式3、文件循环4、文件的修改5、flush方法6、whit语句7、其它操作二、文件夹 /1、提要2、判断路径或文件3、路径名、文件名分割4、工作目录及创建文件...

    2024-02-01 05:21:09
  • 【5G系列】Network Slicing学习总结(2)

    【5G系列】Network Slicing学习总结(2)

    Network Slicing种类

    2024-02-01 05:21:02
  • 写代码常见错误总结

    1. 同时使用&#39; &#39; 和 &quot; &quot;的时候,一定不能全部用单引号或者全部用双引号。 2. xpath定位的时候,需要通过下面的方式,传参数进去: domainiptab...

    2024-02-01 05:20:55
  • 为什么html中文是乱码?html中文乱码怎么解决 热门推荐

    为什么html中文是乱码?html中文乱码怎么解决 热门推荐

    很多程序员在开发网站的时候,都会遇到html文件出现乱码,那么,为什么html中文是乱码?html乱码怎么造成的?html中文乱码怎么解决呢,下面php高手为您解决这些问题。 打造全网web前端全栈资...

    2024-02-01 05:20:25
  • Windows下Scala+Spark+IDEA+Hadoop环境搭建

    Windows下Scala+Spark+IDEA+Hadoop环境搭建

    下载安装包,添加环境变量不再赘述。注意spark和scala要对应版本。例如Spark使用官网下载的spark-2.4.4-bin-hadoop2.7,打开%SPARK_HOME%\jars文件夹,里...

    2024-02-01 05:20:17