终止中断关闭正在执行的方法
直接上代码:
import org.dromara.hutool.core.collection.ListUtil;
import org.dromara.hutool.core.thread.ThreadUtil;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class 任务延迟等待以及终止 {
//这里是JDK21的方法, 低版本的 可以用别的
public static ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
public static long start = System.currentTimeMillis(); //获取执行到这行代码的系统时间
public static List<Integer> 超时集合 = ListUtil.of(5000, 10000);
/**
* @param args
*/
public static void main(String[] args) {
try{
String result = 启动任务(); //调用timeoutMethod(timeout)方法,并传一个实参
System.out.println("整个方法实际耗时:" + (System.currentTimeMillis() - start) + "毫秒"); //打印执行完timeoutMethod(timeout)的方法时间
System.out.println("结果:" + result);
}catch(Exception e){
e.printStackTrace();
System.out.println("异常");
}
System.out.println("60 * 1000 * 100000");
ThreadUtil.sleep(60 * 1000 * 100000);
}
/**
* 有超时时间的方法
* @return
*/
private static String 启动任务() throws Exception{
String result = "false";
/**
* 一个可取消的异步计算。该类提供了一个基类
*/
FutureTask<String> futureTask = new FutureTask<>(new Callable<String>() {
@Override
public String call() throws Exception {
//TODO
boolean bool = 模拟阻塞执行的方法();
return "false";
}
});
// Thread futureTask = new Thread(() -> {
// //TODO
// try {
// boolean bool = unknowMethod(); //调用unkonwMethod(time)方法
// } catch (Exception e) {
// throw new RuntimeException(e);
// }
// });
//开始执行任务
executor.execute(futureTask); //池化线程或调用中执行futureTask
ThreadUtil.sleep(6000);
for (int i = 0; i < 超时集合.size(); i++) {
Integer time = 超时集合.get(i);
try {
//这个方法会阻塞,直到任务完成时会返回
result = futureTask.get(time, TimeUnit.MILLISECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
/**
* 超时之后会进入这里 进行业务处理
*/
//e.printStackTrace();
//TODO 这里通知用户 是否等待
if(i == 0){
System.out.println("第一次 超时 用户点击继续等待");
}
if(i == 1){
System.out.println("第二次 超时 用户点击跳过");
/**
* 强行终止该任务
* 为false时,会等待这个任务执行完,返回true;若任务还没执行,取消任务后返回true,如任务执行完,返回false
*/
futureTask.cancel(true);
System.out.println("任务已终止");
System.out.println("方法实际执行时间:" + (System.currentTimeMillis() - start) + "毫秒");
executor.shutdown();
}
}
}
return result;
}
/**
* 这个方法的耗时
* @return
*/
private static boolean 模拟阻塞执行的方法 () throws Exception{
for (int i = 0; i < 100000000; i++) {
System.out.println("执行中..." + i);
Thread.sleep(1000);
}
System.out.println("任务需要耗时: " + 0 + "毫秒" + "___标志");
return true;
}
}
我们先来了解下同步、异步有什么区别?
同步(Sync)
所谓同步,就是发出一个功能调用时,在没有得到结果之前,该调用就不返回或继续执行后续操作。
根据这个定义,Java中所有方法都是同步调用,应为必须要等到结果后才会继续执行。我们在说同步、异步的时候,一般而言是特指那些需要其他端协作或者需要一定时间完成的任务。
简单来说,同步就是必须一件一件事做,等前一件做完了才能做下一件事。
异步(Async)
异步与同步相对,当一个异步过程调用发出后,调用者在没有得到结果之前,就可以继续执行后续操作。当这个调用完成后,一般通过状态、通知和回调来通知调用者。对于异步调用,调用的返回并不受调用者控制。
对于通知调用者的三种方式,具体如下:
情况 | 作用 |
---|---|
状态 | 即监听被调用者的状态(轮询),调用者需要每隔一定时间检查一次,效率会很低。 |
通知 | 当被调用者执行完成后,发出通知告知调用者,无需消耗太多性能。 |
回调 | 与通知类似,当被调用者执行完成后,会调用调用者提供的回调函数。 |
FutureTask一个可取消的异步计算
,FutureTask 实现了Future的基本方法,可以查询计算是否已经完成,并且可以获取计算的结果。结果只可以在计算完成之后
获取,get方法会阻塞
当计算没有完成的时候,一旦计算已经完成,那么计算就不能再次启动或是取消。
一个FutureTask 可以用来包装一个 Callable 或是一个runnable对象。因为FurtureTask实现了Runnable方法,所以一个 FutureTask可以提交(submit)给一个Excutor执行(excution).
FutureTask可用于异步
获取执行结果或取消执行任务的场景。通过传入Runnable或者Callable的任务给FutureTask,直接调用其run方法或者放入线程池执行,之后可以在外部通过FutureTask的get方法异步获取执行结果,因此,FutureTask非常适合用于耗时
的计算,主线程可以在完成自己的任务后,再去获取结果。另外,FutureTask还可以确保即使调用了多次run方法,它都只会执行一次Runnable或者Callable任务,或者通过cancel取消FutureTask的执行等。
利用FutureTask和ExecutorService,可以用多线程的方式提交计算任务,主线程继续执行其他任务,当主线程需要子线程的计算结果时,在异步获取子线程的执行结果。
?
情况 | 作用 |
---|---|
report(int s) | 返回已完成任务的结果或引发异常。 |
isCancelled() | 判断task是否在正常完成前取消掉了。 |
isDone() | 返回ture或false |
cancel(boolean mayInterruptIfRunning) | 取消task的执行,如果task已完成、已被取消、或者由于某些原因取消不了,则返回false |
get() | 等待task完成,并获取执行结果。可能会抛出异常 |
get(long timeout, TimeUnit unit) | 在指定的时间内等待task完成,并获取执行结果。可能抛出异常(含TimeoutException) |