Future - 可以取消的任务
场景
在合并多个请求时,一般主函数都 会限制超时时间,如果超时,我们就认为子任务请求结束,但是,其实子任务可能还未真正结束,还在后台运行,且占用我们的线程数量。
现在要做的是如果主函数请求超时,那么对应的子任务也要被取消
CancelTaskManager
public class CancelTaskManager {
private List<Future<?>> futures;
private CountDownLatch latch;
public CancelTaskManager(int taskSize) {
this.futures = new LinkedList<>();
latch = new CountDownLatch(taskSize);
}
public <T> CancelTaskManager addTask(Supplier<T> supplier, ExecutorService executor){
Future<T> future = executor.submit(new InterruptableTask<T>(latch) {
@Override
public T get() {
return supplier.get();
}
});
futures.add(future);
return this;
}
public void execute(long timeOut, TimeUnit unit, Consumer<List<Object>> callback)
throws ExecutionException, InterruptedException, TimeoutException {
try {
latch.await(timeOut, unit);
} catch (InterruptedException e) {
// ignore
}
boolean done = true;
for (Future<?> future : futures) {
if (!future.isDone()) {
done = false;
future.cancel(true);
System.out.println("cancel " + future.hashCode());
}
}
List<Object> result = new ArrayList<>();
if (done) {
for (Future<?> future : futures) {
result.add(future.get(0, TimeUnit.SECONDS));
}
callback.accept(result);
}
}
public abstract class InterruptableTask<T> implements Callable<T>, Supplier<T> {
private CountDownLatch latch;
public InterruptableTask(CountDownLatch latch) {
this.latch = latch;
}
@Override
public T call() throws Exception {
try {
T t = get();
return t;
} finally {
latch.countDown();
}
}
}
public static void main(String[] args) throws InterruptedException {
long l = System.currentTimeMillis();
CancelTaskManager futureProvider = new CancelTaskManager(2);
ExecutorService executorService = Executors.newFixedThreadPool(4);
try {
futureProvider.addTask(() -> {
System.out.println("1....start");
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
System.out.println("1....on error");
Thread.currentThread().interrupt();
}
System.out.println("1....end");
System.out.println(Thread.currentThread().getName() + " 耗时: " + (System.currentTimeMillis() - l) / 1000);
return "success";
},executorService).addTask(() -> {
System.out.println("2....start");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
System.out.println("2....on error");
Thread.currentThread().interrupt();
}
System.out.println("2....end");
System.out.println(Thread.currentThread().getName() + " 耗时: " + (System.currentTimeMillis() - l) / 1000);
return 1;
},executorService).execute(6L, TimeUnit.SECONDS, list -> {
System.out.println("正常结束" + list);
System.out.println(Thread.currentThread().getName() + " 耗时: " + (System.currentTimeMillis() - l) / 1000);
});
} catch (Exception e) {
System.out.println("main.....error" + e.getMessage());
System.out.println("main 耗时: " + (System.currentTimeMillis() - l) / 1000);
}
TimeUnit.SECONDS.sleep(15);
executorService.shutdownNow();
}
}
结果:
1....start
2....start
2....end
pool-1-thread-2 耗时: 3
1....end
pool-1-thread-1 耗时: 4
正常结束[success, 1]
main 耗时: 4
没有问题,子任务在6秒内都成功返回了
将第一个任务睡眠时间设置成 15秒
System.out.println("1....start");
try {
TimeUnit.SECONDS.sleep(15);
} catch (InterruptedException e) {
System.out.println("1....on error");
Thread.currentThread().interrupt();
}
System.out.println("1....end");
System.out.println(Thread.currentThread().getName() + " 耗时: " + (System.currentTimeMillis() - l) / 1000);
return "success";
结果
1....start
2....start
2....end
pool-1-thread-2 耗时: 3
cancel 1681595665
1....on error
1....end
pool-1-thread-1 耗时: 6
可以发现,子任务1 虽然睡眠了15秒,但是由于主线程设置了6秒超时,所以在6秒的时候就被中断取消了