Java 8的CompletableFuture提供了强大的task管理能力,比如通知机制,以及更加简单抽象的task管理。
本文中,将用CompletableFuture的 supplyAsync()
方法来创建一个异步task,task会完成特定的工作,并且返回一个String。
下面的 handleResult()
方法作为task完成时的回调函数(注意:task完成并不意味着task的实际工作一定已经运行结束了,比如timeout和cancel场景)。
public static void handleResult(String result) {
System.out.println("==========result: " + result + "===========");
}
一般来说,task的完成,有以下几种原因:
本文将以代码示例,如何处理task的不同完成状态。
注:本文中使用的有些方法(比如 completeOnTimeout()
),是Java 9提供的。
使用 thenAccept()
方法来处理task结果,代码如下:
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "hello");
future.thenAccept(e -> handleResult(e));
try {
Thread.sleep(10* 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
注:在主线程里面sleep一段时间,其目的是确保task在主线程结束前完成工作。
运行结果如下:
==========result: hello===========
Process finished with exit code 0
使用 exceptionally()
方法来处理task的异常,代码如下:
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
throw new RuntimeException("xxx");
});
// future.thenAccept(e -> handleResult(e)); // will not take effect
future.exceptionally(e -> {
System.out.println("got exception: " + e.getClass() + ", " + e.getCause());
handleResult("default exception result");
return "default exception result";
});
try {
Thread.sleep(10* 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
运行结果如下:
got exception: class java.util.concurrent.CompletionException, java.lang.RuntimeException: xxx
==========result: default exception result===========
使用 handle()
方法,它有2个参数:task结果和task异常,代码如下:
future.handle((r, e) -> {
if (e != null) {
System.out.println("got exception: " + e.getClass() + ", " + e.getCause());
handleResult("default exception result");
return "default exception result";
} else {
handleResult(r);
return r;
}
});
使用 completeOnTimeout()
方法,设置timeout时间,并且在timeout发生时指定task结果。代码如下:
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(5 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "hello";
});
future.completeOnTimeout("default timeout result", 3 * 1000, TimeUnit.MILLISECONDS);
future.handle((r, e) -> {
if (e != null) {
System.out.println("got exception: " + e.getClass() + ", " + e.getCause());
handleResult("default exception result");
return "default exception result";
} else {
handleResult(r);
return r;
}
});
try {
Thread.sleep(10* 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
运行结果如下:
==========result: default timeout result===========
注意:在 handle()
方法里走的是正常流程,而不是异常流程。
注:也可以用 orTimeout()
方法,指定timeout时间,则在timeout发生时,task会抛出 TimeoutException
异常。
注意:timeout并不会真正停止task的运行,也不会给task发interrupt信号。
使用 cancel()
方法来cancel task,并抛出 CancellationException
异常。代码如下:
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(5 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "hello";
});
future.handle((r, e) -> {
if (e != null) {
System.out.println("got exception: " + e.getClass() + ", " + e.getCause());
handleResult("default exception result");
return "default exception result";
} else {
handleResult(r);
return r;
}
});
try {
Thread.sleep(3 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
future.cancel(true);
try {
Thread.sleep(10* 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
运行结果如下:
got exception: class java.util.concurrent.CancellationException, null
==========result: default exception result===========
注意:cancel操作的触发时机不可知,一般是在类之外被触发的,所以本例中把cancel操作放在了最后(前面都是对future的基本操作)。
注意:cancel时,在 handle()
方法里走的是异常流程,其Exception为 CancellationException
。
注意:timeout并不会真正停止task的运行,也不会给task发interrupt信号。
现在,考虑所有4种情况,把逻辑处理合到一起,代码如下:
package com.example.test0721;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
public class Test0724 {
public static void handleResult(String result) {
System.out.println("============result: " + result + "=============");
}
public static void main(String[] args) {
Logger log = LoggerFactory.getLogger(Test0724.class);
log.info("main: started");
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
log.info("async: started");
try {
Thread.sleep(10 * 1000);
} catch (InterruptedException e) {
log.info("async: interrupted");
return "default interrupted result";
}
boolean exceptional = false;
if (exceptional) {
log.info("async: run into exception");
throw new RuntimeException("async exception");
} else {
log.info("async: finished");
return "hello";
}
}).completeOnTimeout("default timeout result", 100 * 1000, TimeUnit.MILLISECONDS);
// attention: split the line, because the cancel operation is against the above "future"
future.handle((result, throwable) -> {
log.info("async: result: " + result + ", throwable: " + throwable);
if (throwable != null) {
log.info("async: got exception from async: " + throwable.getClass() + ", " + throwable.getCause());
handleResult("default exception result");
return "default exception result";
} else {
log.info("got normal result: " + result);
handleResult(result);
return result;
}
});
// try {
// Thread.sleep(2 * 1000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
//
// log.info("default cancel value");
// future.cancel(true);
// log.info("main: async cancelled");
try {
Thread.sleep(15 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("main ended");
}
}
直接运行代码,就是正常流程,运行结果如下:
02:33:39.239 [main] INFO com.example.test0721.Test0724 - main: started
02:33:39.259 [ForkJoinPool.commonPool-worker-3] INFO com.example.test0721.Test0724 - async: started
02:33:49.265 [ForkJoinPool.commonPool-worker-3] INFO com.example.test0721.Test0724 - async: finished
02:33:49.298 [ForkJoinPool.commonPool-worker-3] INFO com.example.test0721.Test0724 - async: result: hello, throwable: null
02:33:49.299 [ForkJoinPool.commonPool-worker-3] INFO com.example.test0721.Test0724 - got normal result: hello
============result: hello=============
02:33:54.268 [main] INFO com.example.test0721.Test0724 - main ended
把 exceptional
变量设置为 true
:
boolean exceptional = true;
运行结果如下:
02:34:23.351 [main] INFO com.example.test0721.Test0724 - main: started
02:34:23.368 [ForkJoinPool.commonPool-worker-3] INFO com.example.test0721.Test0724 - async: started
02:34:33.371 [ForkJoinPool.commonPool-worker-3] INFO com.example.test0721.Test0724 - async: run into exception
02:34:33.410 [ForkJoinPool.commonPool-worker-3] INFO com.example.test0721.Test0724 - async: result: null, throwable: java.util.concurrent.CompletionException: java.lang.RuntimeException: async exception
02:34:33.411 [ForkJoinPool.commonPool-worker-3] INFO com.example.test0721.Test0724 - async: got exception from async: class java.util.concurrent.CompletionException, java.lang.RuntimeException: async exception
============result: default exception result=============
02:34:38.373 [main] INFO com.example.test0721.Test0724 - main ended
把task的timeout时间设置为5秒钟:
Thread.sleep(5 * 1000);
运行结果如下:
02:35:27.985 [main] INFO com.example.test0721.Test0724 - main: started
02:35:27.996 [ForkJoinPool.commonPool-worker-3] INFO com.example.test0721.Test0724 - async: started
02:35:33.040 [CompletableFutureDelayScheduler] INFO com.example.test0721.Test0724 - async: result: default timeout result, throwable: null
02:35:33.042 [CompletableFutureDelayScheduler] INFO com.example.test0721.Test0724 - got normal result: default timeout result
============result: default timeout result=============
02:35:37.999 [ForkJoinPool.commonPool-worker-3] INFO com.example.test0721.Test0724 - async: run into exception
02:35:43.007 [main] INFO com.example.test0721.Test0724 - main ended
注意:timeout并不会真正停止task的运行,也不会给task发interrupt信号。本例中,由于把 exception
设置为 true
,可以看到console有 async: run into exception
的输出。同理,假设task没有抛异常,则最终将会在console显示 async: finished
。
把下面的代码反注释:
try {
Thread.sleep(2 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("default cancel value");
future.cancel(true);
log.info("main: async cancelled");
handleResult("default cancel value");
即:在task运行到2秒钟的时候,cancel task。运行结果如下:
02:41:03.815 [main] INFO com.example.test0721.Test0724 - main: started
02:41:03.841 [ForkJoinPool.commonPool-worker-3] INFO com.example.test0721.Test0724 - async: started
02:41:05.841 [main] INFO com.example.test0721.Test0724 - default cancel value
02:41:05.870 [main] INFO com.example.test0721.Test0724 - async: result: null, throwable: java.util.concurrent.CancellationException
02:41:05.871 [main] INFO com.example.test0721.Test0724 - async: got exception from async: class java.util.concurrent.CancellationException, null
============result: default exception result=============
02:41:05.875 [main] INFO com.example.test0721.Test0724 - main: async cancelled
02:41:13.842 [ForkJoinPool.commonPool-worker-3] INFO com.example.test0721.Test0724 - async: run into exception
02:41:20.876 [main] INFO com.example.test0721.Test0724 - main ended
注意:cancel并不会真正停止task的运行,也不会给task发interrupt信号。本例中,由于把 exception
设置为 true
,可以看到console有 async: run into exception
的输出。同理,假设task没有抛异常,则最终将会在console显示 async: finished
。
注意:本例中没有区分task自身的异常和cancel task造成的异常。若想取分的话,只需在 handle()
方法里对异常加以判断。timeout异常也同理。