我知道CompletableFuture
设计不能通过中断来控制它的执行,但我想你们中的一些人可能会遇到这个问题。 CompletableFuture
是组合异步执行的非常好的方法,但考虑到你希望在取消将来时中断或停止底层执行的情况,我们该怎么做?或者我们必须接受任何取消或手动完成的CompletableFuture
不会影响在那里完成它的线程吗?
在我看来,这显然是一项无用的工作,需要时间来执行工人。我想知道在这种情况下哪种方法或设计可能会有所帮助?
更新
这是一个简单的测试
public class SimpleTest {
@Test
public void testCompletableFuture() throws Exception {
CompletableFuture<Void> cf = CompletableFuture.runAsync(()->longOperation());
bearSleep(1);
//cf.cancel(true);
cf.complete(null);
System.out.println("it should die now already");
bearSleep(7);
}
public static void longOperation(){
System.out.println("started");
bearSleep(5);
System.out.println("completed");
}
private static void bearSleep(long seconds){
try {
TimeUnit.SECONDS.sleep(seconds);
} catch (InterruptedException e) {
System.out.println("OMG!!! Interrupt!!!");
}
}
}
答案 0 :(得分:14)
CompletableFuture
与最终可能完成它的异步操作无关。
因为(与
FutureTask
不同)这个类没有直接控制 导致它完成的计算,取消被视为 只是另一种特殊完成形式。方法cancel
有 效果与completeExceptionally(new CancellationException())
相同。
甚至可能 一个单独的线程正在努力完成它(甚至可能有许多线程正在处理它)。即使有,也没有从CompletableFuture
到任何引用它的线程的链接。
因此,您无法通过CompletableFuture
中断任何可能正在运行某个任务的线程来完成它。您必须编写自己的逻辑来跟踪任何Thread
实例,这些实例获取对CompletableFuture
的引用以完成它。
这是我认为你可以逃脱的执行类型的一个例子。
public static void main(String[] args) throws Exception {
ExecutorService service = Executors.newFixedThreadPool(1);
CompletableFuture<String> completable = new CompletableFuture<>();
Future<?> future = service.submit(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
if (Thread.interrupted()) {
return; // remains uncompleted
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
return; // remains uncompleted
}
}
completable.complete("done");
}
});
Thread.sleep(2000);
// not atomic across the two
boolean cancelled = future.cancel(true);
if (cancelled)
completable.cancel(true); // may not have been cancelled if execution has already completed
if (completable.isCancelled()) {
System.out.println("cancelled");
} else if (completable.isCompletedExceptionally()) {
System.out.println("exception");
} else {
System.out.println("success");
}
service.shutdown();
}
这假定正在执行的任务被设置为正确处理中断。
答案 1 :(得分:2)
这个怎么样?
public static <T> CompletableFuture<T> supplyAsync(final Supplier<T> supplier) {
final ExecutorService executorService = Executors.newFixedThreadPool(1);
final CompletableFuture<T> cf = new CompletableFuture<T>() {
@Override
public boolean complete(T value) {
if (isDone()) {
return false;
}
executorService.shutdownNow();
return super.complete(value);
}
@Override
public boolean completeExceptionally(Throwable ex) {
if (isDone()) {
return false;
}
executorService.shutdownNow();
return super.completeExceptionally(ex);
}
};
// submit task
executorService.submit(() -> {
try {
cf.complete(supplier.get());
} catch (Throwable ex) {
cf.completeExceptionally(ex);
}
});
return cf;
}
简单测试:
CompletableFuture<String> cf = supplyAsync(() -> {
try {
Thread.sleep(1000L);
} catch (Exception e) {
System.out.println("got interrupted");
return "got interrupted";
}
System.out.println("normal complete");
return "normal complete";
});
cf.complete("manual complete");
System.out.println(cf.get());
我不喜欢每次都要创建Executor服务的想法,但也许你可以找到一种方法来重用ForkJoinPool。
答案 2 :(得分:1)
请参阅我对相关问题的回答: Transform Java Future into a CompletableFuture
在那里提到的代码中,CompletionStage行为被添加到RunnableFuture子类(由ExecutorService实现使用),因此您可以以正确的方式中断它。
答案 3 :(得分:1)
我遇到了类似的问题,需要模拟 InterruptedException。
我模拟了应该返回 CompletetableFuture 的方法调用,并且我对返回值进行了监视,这样 CompletableFuture#get
将抛出异常。
它按我的预期工作,并且我能够测试该代码是否正确处理了异常。
CompletableFuture spiedFuture = spy(CompletableFuture.completedFuture(null));
when(spiedFuture .get()).thenThrow(new InterruptedException());
when(servuce.getById(anyString())).thenReturn(spiedFuture );
答案 4 :(得分:1)
以下是创建可取消的 Future
任务的超短版本:
public static <T> Future<T> supplyAsync(Function<Future<T>, T> operation) {
CompletableFuture<T> future = new CompletableFuture<>();
return future.completeAsync(() -> operation.apply(future));
}
将 CompletableFuture
传递给操作 Function
以便能够检查 Future
的取消状态:
Future<Result> future = supplyAsync(task -> {
while (!task.isCancelled()) {
// computation
}
return result;
});
// later you may cancel
future.cancel(false);
// or retrieve the result
Result result = future.get(5, TimeUnit.SECONDS);
但这不会中断运行操作的 Thread
。如果您还希望能够中断 Thread
,那么您必须存储对它的引用并覆盖 Future.cancel(..)
以中断它。
public static <T> Future<T> supplyAsync(Function<Future<T>, T> action) {
return supplyAsync(action, r -> new Thread(r).start());
}
public static <T> Future<T> supplyAsync(Function<Future<T>, T> action, Executor executor) {
AtomicReference<Thread> interruptThread = new AtomicReference<>();
CompletableFuture<T> future = new CompletableFuture<>() {
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
if (!interruptThread.compareAndSet(null, Thread.currentThread())
&& mayInterruptIfRunning) {
interruptThread.get().interrupt();
}
return super.cancel(mayInterruptIfRunning);
}
};
executor.execute(() -> {
if (interruptThread.compareAndSet(null, Thread.currentThread())) try {
future.complete(action.apply(future));
} catch (Throwable e) {
future.completeExceptionally(e);
}
});
return future;
}
以下测试检查执行我们的 Thread
的 Function
是否被中断:
@Test
void supplyAsyncWithCancelOnInterrupt() throws Exception {
Object lock = new Object();
CountDownLatch done = new CountDownLatch(1);
CountDownLatch started = new CountDownLatch(1);
Future<Object> future = supplyAsync(m -> {
started.countDown();
synchronized (lock) {
try {
lock.wait(); // let's get interrupted
} catch (InterruptedException e) {
done.countDown();
}
}
return null;
});
assertFalse(future.isCancelled());
assertFalse(future.isDone());
assertTrue(started.await(5, TimeUnit.SECONDS));
assertTrue(future.cancel(true));
assertTrue(future.isCancelled());
assertTrue(future.isDone());
assertThrows(CancellationException.class, () -> future.get());
assertTrue(done.await(5, TimeUnit.SECONDS));
}
答案 5 :(得分:0)
那怎么办?
startTimeBox = browser.find_element_by_name('TimeStart')
startTimeBox.get_attribute('outerHTML')
Out[187]: '<input name="TimeStart" id="TimeStart" title="Please enter start time" size="10" maxlength="12" value="00:00:00" type="time">'
startDateBox = browser.find_element_by_name('DateStart')
startDateBox.get_attribute('outerHTML')
Out[185]: '<input name="DateStart" id="DateStart" title="Please enter start date" size="10" maxlength="11" value="09/11/2018" style="" type="date">'
答案 6 :(得分:0)
如果您使用
cf.get();
代替
cf.join();
等待完成的线程可以被中断。这使我陷入困境,所以我只是把它放在那里。然后,您需要进一步传播此中断/使用cf.cancel(...)真正完成执行。