我们在jdk1.8之前,都是用FutureTask的get方法来获取异步执行的结果。
在演示之前,先贴一下共用的代码。
ConcurrentSupport:
public class ConcurrentSupport {
public static String processOne() {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
return getNow() + "#one";
}
public static String processTwo() {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
return getNow() + "#two";
}
public static String processThree() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
return getNow() + "#three";
}
public static String getNow() {
return LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"));
}
}
TaskA:
class TaskA implements Callable {
@Override
public Object call() throws Exception {
return ConcurrentSupport.processOne();
}
}
TaskB:
class TaskB implements Callable {
@Override
public Object call() throws Exception {
return ConcurrentSupport.processTwo();
}
}
TaskC:
class TaskC implements Callable {
@Override
public Object call() throws Exception {
return ConcurrentSupport.processThree();
}
}
普通情况
比如有3个任务,分别耗时1s、2s、3s,在同步执行的时候,整个耗时就是6s。
public class NormalDemo {
public static void main(String[] args) {
long start = System.currentTimeMillis();
System.out.println(ConcurrentSupport.processOne());
System.out.println(ConcurrentSupport.processTwo());
System.out.println(ConcurrentSupport.processThree());
System.out.println("耗时:" + (System.currentTimeMillis() - start));
}
}
FutureTask
用FutureTask的情况,就是取最长时间的那个,所以最终时间是3s。
public class FutureTaskDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 创建线程池
ExecutorService executor = Executors.newFixedThreadPool(3);
long start = System.currentTimeMillis();
// 创建FutureTask
FutureTask taskA = new FutureTask(new TaskA());
FutureTask taskB = new FutureTask(new TaskB());
FutureTask taskC = new FutureTask(new TaskC());
executor.submit(taskA);
executor.submit(taskB);
executor.submit(taskC);
System.out.println(taskC.get());
System.out.println(taskA.get());
System.out.println(taskB.get());
System.out.println("耗时:" + (System.currentTimeMillis() - start));
}
}
CompletionService
但是FutureTask也有一个小瑕疵,比如上面的TaskC执行的时间最长,直接把TaskA和TaskB的打印任务给阻塞了,打印的结果是three、one、two。
有木有办法是哪个任务先执行成功,就先打印(或者对这个结果其他处理)这个结果呢?
CompletionService做的就是这个事情。从结果可以看出打印one、two、three。
public class CompletionServiceDemo {
public static void main(String[] args) throws InterruptedException, ExecutionException {
// 创建线程池
ExecutorService executor = Executors.newFixedThreadPool(3);
long start = System.currentTimeMillis();
// 创建CompletionService
CompletionService cs = new ExecutorCompletionService<>(executor);
// 用于保存Future对象
List> futures = new ArrayList<>(3);
// 提交FutureTask
futures.add(cs.submit(new TaskC()));
futures.add(cs.submit(new TaskA()));
futures.add(cs.submit(new TaskB()));
for (int i = 0; i < 3; i++) {
String result = cs.take().get();
System.out.println(result);
}
System.out.println("耗时:" + (System.currentTimeMillis() - start));
}
}
CompletableFuture
如果有一个任务是这样的,A1执行完执行A2,B1执行完执行B2,A2和B2执行完,再执行C。
JDK1.8提供了CompletableFuture这个优雅的解决方案。
比如下面的例子,就是f1和f2执行完后,才执行f3。
CompletableFuture的方法中,runAsync是没有返回值的,supplyAsync是有返回值的。
public class CompletableFutureDemo {
public static void main(String[] args) {
CompletableFuture f1 = CompletableFuture.runAsync(() -> {
String one = ConcurrentSupport.processOne();
System.out.println(one);
}
);
CompletableFuture f2 = CompletableFuture.supplyAsync(() -> {
String two = ConcurrentSupport.processTwo();
System.out.println(two);
return two;
}
);
CompletableFuture f3 =
f1.thenCombine(f2, (__, tf) -> {
System.out.println("f3#" + tf);
return "f3";
}
);
f3.join();
}
}