今天学了下多线程中超时任务的处理,这里和大家分享下,遇到了点问题没能解决,留下来希望大家帮我解疑啊。

在JAVA中停止线程的方法有多种,有一种是结合ExecutorService和Future的使用,停止在线程池中超时的任务。
这种情况下处理的都是比较耗时的操作,比如请求资源,数据库查询等,当超过一定时间没有返回结果,就结束线程,提高响应速度。

具体步骤如下:

  1. 实现Runnable接口或者Callable接口,分别实现run方法或者call方法,在方法中执行耗时操作。
  2. 将步骤1中的耗时操作线程放入到线程池中(通过ExecutorService.submit方法),这个线程池由ExecutorService来管理。
  3. 步骤2中的submit方法会返回一个Future对象,这个Future对象得到耗时操作的执行结果。Future的get(long timeout, TimeUnit unit)将会在指定的时间内去获得执行结果,如果操作还没执行完,就会抛出TimeoutException,在捕获这个超时异常时就可以取消耗时任务的执行(通过Future.cancel(true)).

下面是一个下载大文件的程序,如果下载任务超时将关闭任务。
(DownloadTask.java: 下载任务)

 
  1. 1 class DownloadTask implements Runnable {
  2. 2 private String filename;
  3. 3 // 接收在run方法中捕获的异常,然后自定义方法抛出异常
  4. 4 private Throwable exception;
  5. 5 //是否关闭此下载任务
  6. 6 private boolean isStop = false;
  7. 7
  8. 8 public void setStop(boolean isStop) {
  9. 9 this.isStop = isStop;
  10. 10 }
  11. 11
  12. 12 public DownloadTask(String filename) {
  13. 13 this.filename = filename;
  14. 14 }
  15. 15
  16. 16 /**
  17. 17 * 下载大数据
  18. 18 *
  19. 19 * @param filename
  20. 20 * @throws FileNotFoundException
  21. 21 * , IOException
  22. 22 */
  23. 23 private void download() throws FileNotFoundException, IOException {
  24. 24 File file = new File(filename);
  25. 25 File saveFile = null;
  26. 26 String[] names = filename.split("/");
  27. 27 String saveName = names[names.length - 1];
  28. 28 saveFile = new File("tmp/" + saveName);
  29. 29 InputStream input = new FileInputStream(file);
  30. 30 OutputStream output = new FileOutputStream(saveFile);
  31. 31
  32. 32 // 进行转存
  33. 33 int len = 0;
  34. 34 byte[] buffer = new byte[1024];
  35. 35 while (-1 != (len = input.read(buffer, 0, buffer.length))) {
  36. 36 if(isStop)
  37. 37 break;
  38. 38 output.write(buffer, 0, len);
  39. 39 }
  40. 40
  41. 41 input.close();
  42. 42 output.close();
  43. 43 }
  44. 44
  45. 45 public void throwException() throws FileNotFoundException, IOException {
  46. 46 if (exception instanceof FileNotFoundException)
  47. 47 throw (FileNotFoundException) exception;
  48. 48 if (exception instanceof IOException)
  49. 49 throw (IOException) exception;
  50. 50 }
  51. 51
  52. 52 @Override
  53. 53 public void run() {
  54. 54 try {
  55. 55 download();
  56. 56 } catch (FileNotFoundException e) {
  57. 57 exception = e;
  58. 58 } catch (IOException e) {
  59. 59 exception = e;
  60. 60 }
  61. 61 }
  62. 62 }
 

注意到第4行定义了一个Throwable对象,这是因为在线程类的run方法中是没办法抛出异常的,要让外部获得程序的运行状况,就通过手动赋值和抛出,第45~49行定义了抛出异常的方法。(任务的业务逻辑不应该在内部处理掉,而应该在外部判断进行控制,个人理解)

(LoadSomething.java: 启动下载任务和处理异常)

 
  1. 1 /**
  2. 2 * 使用Futrue来取消任务,模拟下载文件超时时取消任务
  3. 3 *
  4. 4 * @author hongjie
  5. 5 *
  6. 6 */
  7. 7 public class LoadSomething {
  8. 8 // 线程池服务接口
  9. 9 private ExecutorService executor;
  10. 10
  11. 11
  12. 12 public LoadSomething() {
  13. 13 executor = Executors.newSingleThreadExecutor();
  14. 14 }
  15. 15
  16. 16 public void beginToLoad(DownloadTask task, long timeout,
  17. 17 TimeUnit timeType) {
  18. 18 Future<?> future = executor.submit(task);
  19. 19 try {
  20. 20 future.get(timeout, timeType);
  21. 21 task.throwException();
  22. 22 } catch (InterruptedException e) {
  23. 23 System.out.println("下载任务已经取消");
  24. 24 } catch (ExecutionException e) {
  25. 25 System.out.println("下载中发生错误,请重新下载");
  26. 26 } catch (TimeoutException e) {
  27. 27 System.out.println("下载超时,请更换下载点");
  28. 28 } catch (FileNotFoundException e) {
  29. 29 System.out.println("请求资源找不到");
  30. 30 } catch (IOException e) {
  31. 31 System.out.println("数据流出错");
  32. 32 } finally {
  33. 33 task.setStop(true);
  34. 34 // 因为这里的下载测试不用得到返回结果,取消任务不会影响结果
  35. 35 future.cancel(true);
  36. 36 }
  37. 37 }
  38. 38 }
 

第18行将下载任务放入了线程池中并且开始执行下载任务,第20行中在制定时间内去获得结果,如果超时将会抛出TimeoutException, 可以看到21行是抛出下载任务中所可能遇到的异常。最后在finally块中,调用future.cancle停掉任务。这里不用ExecutorService.shutdown方法是因为可能在线程池中有其他线程正在执行任务。
我遇到的问题:在33行中我加了个task.setStop(true)方法,这里通过改变控制变量状态来停止下载任务的执行,这种方法是可以停止掉下载任务。但是如果不用这种改变控制变量的方法,用future.cancel方法应该是能停掉下载任务的。但实际执行结果是抛出了超时异常,future.cancel返回了true,即任务已取消,但文件还是会继续下载下来,我想会不会是这样,文件下载是IO阻塞的,当下载任务执行的时候,线程会阻塞在那里,所以关不掉??希望知道的朋友指点一下我啊。

(LoadTest.java: 测试主函数)

 
  1. 1 public class LoadTest {
  2. 2 public static void main(String[] args) {
  3. 3 LoadSomething load = new LoadSomething();
  4. 4 DownloadTask downloadTask = new DownloadTask("G:/Games/5211install.exe");
  5. 5 //开始下载,并设定超时限额为3毫秒
  6. 6 load.beginToLoad(downloadTask, 3, TimeUnit.MILLISECONDS);
  7. 7 }
  8. 8 }
 

程序运行结果:
源文件:

抛出异常:

目标文件:

之前对相对路径和绝对路径不是很了解,今天查了一下,有几篇好文章,个人觉得挺不错,也和大家分享下:
JAVA中的相对路径和绝对路径(和虚拟机JVM有关)

问题我已经找到答案了:
在java库中,许多可阻塞的方法都是通过提前返回或者抛出InterruptedException来响应中断请求的,从而使开发人员更容易构建出能响应取消请求的任务。然而并非所有的可阻塞方法或者阻塞机制都能响应中断;如果一个线程由于执行同步的Socket I/O或者等待获得内置锁而阻塞,那么中断请求只能设置线程的中断状态,除此之外没有其他任何作用。

在服务应用程序中,最常见的阻塞I/O形式就是对套接字进行读取和写入。虽然InputStream和fOutputStream中的read和write等方法都不会响应中断,但通过关闭底层的套接字,可以使得由于执行read和write等方法而被阻塞的线程抛出一个socketException。

上面的情况和这个是类似的。

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

JAVA中的相对路径和绝对路径》原文:

File类是用来构造文件或文件夹的类,在其构造函数中要求传入一个String类型的参数,用于指示文件所在的路径.以前一直使用绝对路径作为参数,其实这里也可以使用相对路径.使用绝对路径不用说,很容易就能定位到文件,那么使用了相对路径jvm如何定位文件的呢?

按照jdk Doc上的说法”绝对路径名是完整的路径名,不需要任何其他信息就可以定位自身表示的文件。相反,相对路径名必须使用来自其他路径名的信息进行解释。默认情况下,java.io 包中的类总是根据当前用户目录来分析相对路径名。此目录由系统属性 user.dir 指定,通常是 Java 虚拟机的调用目录.”

相对路径顾名思义,相对于某个路径,那么究竟相对于什么路径我们必须弄明白.按照上面jdk文档上讲的这个路径是”当前用户目录”也就是”java虚拟机的调用目录”.更明白的说这个路径其实是我们在哪里调用jvm的路径.举个例子:

假设有一java源文件Example.java在d盘根目录下,该文件不含package信息.我们进入命令行窗口,然后使用”d:”命令切换到d盘根目录下,然后用”javac Example.java”来编译此文件,编译无错后,会在d盘根目录下自动生成”Example.class”文件.我们在调用”java Example”来运行该程序.此时我们已经启动了一个jvm,这个jvm是在d盘根目录下被启动的,所以此jvm所加载的程序中File类的相对路径也就是相对这个路径的,即d盘根目录:D:\.同时” 当前用户目录”也是D:\.在System.getProperty(“user.dir”);系统变量”user.dir”存放的也是这个值.

我们可以多做几次试验,把”Example.class”移动到不同路径下,同时在那些路径下,执行”java Example”命令启动jvm,我们会发现这个”当前用户目录”是不断变化的,它的路径始终和我们在哪启动jvm的路径是一致的.

搞清了这些,我们可以使用相对路径来创建文件,例如:

File file = new File(“a.txt”);

File.createNewFile();

假设jvm是在”D:\”下启动的,那么a.txt就会生成在D:\a.txt;

此外,这个参数还可以使用一些常用的路径表示方法,例如”.”或”.\”代表当前目录,这个目录也就是jvm启动路径.所以如下代码能得到当前目录完整路径:

File f = new File(“.”);

String absolutePath = f.getAbsolutePath();

System.out.println(absolutePath);//D:\

最后要说说在eclipse中的情况:

Eclipse中启动jvm都是在项目根路径上启动的.比如有个项目名为blog,其完整路径为:D:\work\IDE\workspace\blog.那么这个路径就是jvm的启动路径了.所以以上代码如果在eclipse里运行,则输出结果为” D:\work\IDE\workspace\blog.”

Tomcat中的情况.

如果在tomcat中运行web应用,此时,如果我们在某个类中使用如下代码:

File f = new File(“.”);

String absolutePath = f.getAbsolutePath();

System.out.println(absolutePath);

那么输出的将是tomcat下的bin目录.我的机器就是” D:\work\server\jakarta-tomcat-5.0.28\bin\.”,由此可以看出tomcat服务器是在bin目录下启动jvm的.其实是在bin目录下的”catalina.bat”文件中启动jvm的.

使用Future停止超时任务的更多相关文章

  1. Java定时线程池停止超时任务

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

  2. java 利用Future做超时任务处理

    Callable<String> task = new Callable<String>() { @Override public String call() throws E ...

  3. Scala之Future超时

    最近在开发中使用akka http进行请求,返回的是一个future,并且要对future进行超时设置,不知怎么设置,因此学习了下. 一.Future阻塞 首先,scala中的future不支持内置超 ...

  4. Volley超时重试机制

    基础用法 Volley为开发者提供了可配置的超时重试机制,我们在使用时只需要为我们的Request设置自定义的RetryPolicy即可. 参考设置代码如下: int DEFAULT_TIMEOUT_ ...

  5. Flutter 异步Future与FutureBuilder实用技巧

    什么是Future? Future表示在接下来的某个时间的值或错误,借助Future我们可以在Flutter实现异步操作.它类似于ES6中的Promise,提供then和catchError的链式调用 ...

  6. java并发实践笔记

    底层的并发功能与并发语义不存在一一对应的关系.同步和条件等底层机制在实现应用层协议与策略须始终保持一致.(需要设计级别策略.----底层机制与设计级策略不一致问题). 简介 1.并发简史.(资源利用率 ...

  7. java动态编译 (java在线执行代码后端实现原理)(二)

    在上一篇java动态编译 (java在线执行代码后端实现原理(一))文章中实现了 字符串编译成字节码,然后通过反射来运行代码的demo.这一篇文章提供一个如何防止死循环的代码占用cpu的问题. 思路: ...

  8. gevent For the Working Python Developer

    Gevent指南   gevent程序员指南 由Gevent社区编写 gevent是一个基于libev的并发库.它为各种并发和网络相关的任务提供了整洁的API. 介绍 贡献者 核心部分 Greenle ...

  9. Scala零基础教学【102-111】Akka 实战-深入解析

    第102讲:通过案例解析Akka中的Actor运行机制以及Actor的生命周期 Actor是构建akka程序的核心基石,akka中actor提供了构建可伸缩的,容错的,分布式的应用程序的基本抽象, a ...

随机推荐

  1. linux内核中链表代码分析---list.h头文件分析(二)【转】

    转自:http://blog.chinaunix.net/uid-30254565-id-5637598.html linux内核中链表代码分析---list.h头文件分析(二) 16年2月28日16 ...

  2. 字符串cookies转字典 scrapy使用。

    配置文件 DOWNLOADER_MIDDLEWARES = { 'weibo.middlewares.CookiesMiddleware': 543, } 中间件内容 class CookiesMid ...

  3. ulimit -n 修改

    Linux系统里打开文件描述符的最大值,一般缺省值是1024,对一台繁忙的服务器来说,这个值偏小,所以有必要重新设置linux系统里打开文件描述符的最大值.那么应该在哪里设置呢? [root@loca ...

  4. 004_i686和x86_64的区别

    找回TCL隐藏分区(转载) 用Wubi安装 Ubuntu 出现(Initranfs)问题的解决方案 i686和x86_64的区别 2009-04-11 08:19:31|  分类: 电脑问题 |  标 ...

  5. zabbix实现对tomcat的监控

    zabbix实现对tomcat的监控 工作原理 比如:当Zabbix-Server需要知道java应用程序的某项性能的时候,会启动自身的一个Zabbix-JavaPollers进程去连接Zabbix- ...

  6. Python-HTML CSS 练习

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  7. PYTHON-模块-time&datetime-练习 +目录规范

    # 作业# 1.请写出规范目录# 并解释各文件夹的作用bin 可执行文件conf 配置文件core 主要业务逻辑db 数据文件lib 库(公共代码 第三方模块)log 日志文件 # 2.改造atm + ...

  8. 输入年月日判断是当年第几天(if判断)

  9. 【mongo】登陆报错

    今天登陆mongo时出现了错误 Error: couldn't connect to server 127.0.0.1:27017 (127.0.0.1), connection attempt fa ...

  10. ctype

    original:http://www.runoob.com/cprogramming/c-standard-library-ctype-h.html 下面列出了头文件 ctype.h 中定义的函数: ...