Java - 3种方式实现任务执行超时自动取消功能(限制方法执行时间)
当我们程序在处理长时间运行的任务时(例如网络连接或计算密集型任务时),通常需要设置超时时间,避免由于资源被长时间占用而影响系统其他功能的正常执行。Java 中有多个方式可以实现该功能需求,假设我们有一个如下耗时方法,每隔 1 秒打印一段信息,总共持续 10 秒:
// 耗时的方法 public static Object longRunningMethod() { for (int i = 0; i < 10; i++) { System.out.println("这是第" + (i + 1) + "次打印"); try { //等待1秒 Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); return null; } } return "完成"; }下面分别使用 3 种方式来实现任务超时取消功能。
1,使用线程池和 Future 实现
(1)该方法是通过基于异步任务结果的获取来实现的,具体流程如下:
- 首先创建了一个新的线程池 executor 和一个新的 CompletionService,用于监控要限制时间的方法的执行情况。
- 然后,我们使用 completionService.submit() 方法提交要执行的方法,该方法会返回一个 Future 对象。
- 接着使用 completionService.poll() 方法来获取执行结果。如果该方法在超时时间内执行完毕,则会返回执行结果,我们可以对执行结果进行处理;如果该方法超时了,可以调用 Future 对象的 cancel() 方法来取消任务。
(2)样例代码如下:
public class Test { public static void main(String[] args) { // 定义超时时间为3秒 long timeout = 3000; // 创建一个新的线程池,用于执行要限制时间的方法 Executor executor = Executors.newSingleThreadExecutor(); // 创建一个新的CompletionService,用于监控执行时间 CompletionService<Object> completionService = new ExecutorCompletionService<>(executor); // 提交要执行的方法 Future<?> future = completionService.submit(new Callable<Object>() { public Object call() throws Exception { // 这里是要执行的方法 return longRunningMethod(); } }); // 获取执行结果 try { Object result = completionService.poll(timeout, TimeUnit.MILLISECONDS); if (result == null) { System.out.println("超时了,结束该方法的执行"); future.cancel(true); } else { // 方法执行完毕,处理执行结果 System.out.println("方法执行完毕,结果:" + result.toString()); } } catch (InterruptedException e) { System.out.println("出现异常,结束该方法的执行"); future.cancel(true); } } }
2,使用 FutureTask 实现
(1)我们还可以使用 Java 5 新增的 java.util.concurrent.FutureTask 类来限制方法的执行时间,具体流程如下:
- 首先创建了一个 FutureTask 对象,用于监控要限制时间的方法的执行情况。
- 然后使用 new Thread(futureTask).start() 方法在新线程中执行要限制时间的方法,并使用 futureTask.get() 方法来获取执行结果。如果该方法在超时时间内执行完毕,则会返回执行结果。如果该方法超时了,则会抛出异常,我们在异常处理中调用 futureTask.cancel() 方法取消任务。
(2)样例代码如下:
public class Test { public static void main(String[] args) { // 定义超时时间为3秒 long timeout = 3000; // 创建一个新的FutureTask FutureTask<Object> futureTask = new FutureTask<Object>(new Callable<Object>() { public Object call() throws Exception { // 这里是要执行的方法 return longRunningMethod(); } }); // 提交要执行的方法 new Thread(futureTask).start(); // 获取执行结果 try { Object result = futureTask.get(timeout, TimeUnit.MILLISECONDS); System.out.println("方法执行完毕,结果:" + result.toString()); } catch (InterruptedException e) { System.out.println("出现异常,结束该方法的执行"); futureTask.cancel(true); } catch (ExecutionException e) { System.out.println("出现异常,结束该方法的执行"); futureTask.cancel(true); } catch (TimeoutException e) { // 超时了,结束该方法的执行 System.out.println("超时了,结束该方法的执行"); futureTask.cancel(true); } } }
3,使用 CompletableFuture 实现
(1)最简单的方法还是使用 Java 8 新增的 java.util.concurrent.CompletableFuture 类来限制方法的执行时间,具体流程如下:
- 首先创建了一个 CompletableFuture 对象,用于监控要限制时间的方法的执行情况。
- 然后,我们使用 CompletableFuture.supplyAsync() 方法提交要执行的方法,并使用 future.get() 方法来获取执行结果。如果该方法在超时时间内执行完毕,则会返回执行结果。如果该方法超时了,则会抛出异常,我们在异常处理中调用 future.cancel() 方法取消任务。
(2)样例代码如下:
public class Test { public static void main(String[] args) { // 定义超时时间为3秒 long timeout = 3000; // 创建一个新的CompletableFuture CompletableFuture<Object> future = CompletableFuture.supplyAsync(() -> { // 这里是要执行的方法 return longRunningMethod(); }); // 获取执行结果 try { Object result = future.get(timeout, TimeUnit.MILLISECONDS); System.out.println("方法执行完毕,结果:" + result.toString()); } catch (InterruptedException e) { System.out.println("出现异常,结束该方法的执行"); future.cancel(true); } catch (ExecutionException e) { System.out.println("出现异常,结束该方法的执行"); future.cancel(true); } catch (TimeoutException e) { // 超时了,结束该方法的执行 System.out.println("超时了,结束该方法的执行"); future.cancel(true); } } }