Related Reading:
Java concurrent programming (1) Knowledge Map
Java concurrent programming (2) Atomic
Java concurrent programming (3) Visibility
Java concurrent programming (4) Ordered
Java concurrent programming (5) Overview of thread creation methods
Introduction to Java concurrent programming (6) Synchronization usage
Introduction to Java concurrent programming (7) Easy to understand wait and notify and usage scenarios
Introduction to Java concurrent programming (8) Thread life cycle
Introduction to Java concurrent programming (9) Deadlock and deadlock bit
Introduction to Java concurrent programming (10) Lock optimization
Java Introduction to Concurrent Programming (Eleven) Current Limiting Scenarios and Spring Current Limiter Implementation of
Java Concurrent Programming (Twelve) Producer and Consumer Mode-Code Template
Java Concurrent Programming (Thirteen) Read-write Lock and Cache Template
Java Concurrent Programming Introduction (fourteen) CountDownLatch application scenario
Java concurrent programming introduction (fifteen) CyclicBarrier application scenario
Java concurrent programming introduction (sixteen) seconds to understand the difference between the thread pool
Java concurrent programming introduction (seventeen) a picture master thread common classes and interfaces
Java concurrency Introduction to Programming (18) Re-discussion on Thread Safety
1. Introduction to CompleteFeature
CompleteFeature is an enhancement to Feature. Feature can only handle simple asynchronous tasks, and CompleteFeature can combine multiple asynchronous tasks in a complex combination, supporting serial execution, parallel execution, And aggregation, and Or aggregation, so that it can handle complex related tasks Perform scheduling.
2. Business scenarios supported by CompleteFeature
2.1. Serial tasks
The serial task refers to that task B waits for task A to complete execution. The serial task has the following attributes:
Attributes | description |
---|---|
A result can be obtained | Task B can obtain the execution result of task A as a parameter |
B has a return value | If task B has a return value, you can return the execution result through the return value |
Available A exception | Task B can get the exception thrown by task A |
A abnormal termination | When task A throws an exception, whether the program will terminate, if it will terminate, the program will exit, task B will not execute, otherwise the program will not exit and continue to execute. |
The serial task methods supported by CompleteFeature are as follows:
method | A result can be obtained | B has a return value | Available A exception | A abnormal termination |
---|---|---|---|---|
thenRun | no | no | no | Yes |
thenApply | Yes | Yes | no | Yes |
thenAccept | Yes | no | no | Yes |
thenCompose | Yes | Yes | no | Yes |
whenComplete | Yes | no | Yes | no |
exceptionally | no | Yes | Yes | no |
handle | Yes | Yes | Yes | no |
to sum up:
- The task will use the first four methods without throwing an exception, otherwise use the last three methods.
- exceptionally equivalent to the catch part of try {} catch {}, whenComplete and handle are equivalent to the catch and final part of try {} catch {} finally {}, the difference is that one has a return value, and the other has no return value.
- The difference between thenApply and thenCompose is that thenCompose returns CompletableFuture in task B, you can refer to the following examples to compare the differences.
1.2. And convergence
And the aggregation relationship means that task C needs to wait for task A or task B to be executed before being executed. CompleteFeature supports this relationship as follows:
method | C receives the return value of A or B as a parameter | C has a return value |
---|---|---|
thenCombine | Yes | Yes |
thenAcceptBoth | Yes | no |
runAfterBoth | no | no |
1.3. Or convergence
Or aggregation relationship means that task C waits for one of task A or task B to be executed, that is, C only needs to wait for the first task to be executed. CompleteFeature supports this relationship as follows:
method | C receives the return value of A or B as a parameter | C has a return value |
---|---|---|
applyToEither | Yes | Yes |
acceptEither | Yes | no |
runAfterEither | no | no |
1.4. Multitasking
CompletableFuture provides two multitasking methods:
method | description |
---|---|
anyOf | After any one of the multiple tasks is executed, it ends, and the return value of the first executed task can be obtained. |
allOf | Multiple tasks are completed after execution, and the return value of any task cannot be obtained |
The return values of all the above methods are CompletableFuture, so that you can continue to call the methods described above to perform task combination, and combine more complex task processing flows.
1.5. Method family
The last task in the above method is executed in the same thread as the previous task. There is a set of methods in CompletableFuture to let the last task be executed in a new thread, as long as the original method is suffixed with Async, for example:
Synchronize | asynchronous |
---|---|
thenApply | thenApplyAsync |
thenAccept | thenAcceptAsync |
thenRun | thenRunAsync |
thenCompose | thenComposeAsync |
For details, please refer to the source code.
2. Code examples
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class CompleteFeatureDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
simpleTask();
serialTask();
andTask();
orTask();
complexTask();
sleep(2000); // 等待子线程结束
System.out.println("end.");
}
private static void simpleTask() throws ExecutionException, InterruptedException {
// 1. runAsync 执行一个异步任务,没有返回值
CompletableFuture.runAsync(()-> System.out.println("1. runAsync"));
sleep(100);
// 2. supplyAsync 执行一个异步任务,有返回值
CompletableFuture<String> future = CompletableFuture.supplyAsync(()->{
System.out.println("2.1 supplyAsync task be called");
sleep(100);
return "2.2 supplyAsync return value";
});
System.out.println("2.3 after supplyAsync");
System.out.println(future.get());
sleep(200);
}
private static void serialTask() throws ExecutionException, InterruptedException {
// 3. thenRun
CompletableFuture.supplyAsync(()->{
System.out.println("3.1 supplyAsync begin");
sleep(100); // 用于证明B等待A结束才会执行
return "3.2 supplyAsync end";
}).thenRun(()->{
System.out.println("3.3 thenRun be called.");
});
sleep(200);
// 4. thenApply
CompletableFuture<String> future4 = CompletableFuture.supplyAsync(()->{
sleep(100);
return "4.1 apple";
}).thenApply(returnVal->{
return "4.2 " + returnVal + "-苹果";
});
System.out.println("4.3 get: " + future4.get());
sleep(100);
// 5. thenAccept
CompletableFuture.supplyAsync(()->{
sleep(100);
return "5.1 orange";
}).thenAccept(returnVal->{
System.out.println("5.2 " + returnVal + "-桔子");
});
sleep(100);
// 6. thenCompose
CompletableFuture<String> future6 = CompletableFuture.supplyAsync(()->{
sleep(100);
return "6.1 apple";
}).thenCompose((returnVal)->{
return CompletableFuture.supplyAsync(()->{
return "6.2 " + returnVal;
});
});
System.out.println("6.3 get: " + future6.get());
sleep(100);
// 7. whenComplete
CompletableFuture.supplyAsync(()->{
sleep(100);
if (true) { //修改boolean值观察不同结果
return "7.1 return value for whenComplete";
} else {
throw new RuntimeException("7.2 throw exception for whenComplete");
}
}).whenComplete((returnVal, throwable)->{
System.out.println("7.2 returnVal: " + returnVal); // 可以直接拿到返回值,不需要通过future.get()得到
System.out.println("7.3 throwable: " + throwable); // 异步任务抛出异常,并不会因为异常终止,而是会走到这里,后面的代码还会继续执行
});
sleep(100);
// 8. exceptionally
CompletableFuture<String> future8 = CompletableFuture.supplyAsync(()->{
sleep(100);
if (false) { //修改boolean值观察不同结果
return "8.1 return value for exceptionally";
} else {
throw new RuntimeException("8.2 throw exception for exceptionally");
}
}).exceptionally(throwable -> {
throwable.printStackTrace();
return "8.3 return value after dealing exception.";
});
System.out.println("8.4 get: " + future8.get());
sleep(100);
// 9. handle
CompletableFuture<String> future9 = CompletableFuture.supplyAsync(()->{
sleep(100);
if (false) { //修改boolean值观察不同结果
return "9.1 return value for handle";
} else {
throw new RuntimeException("9.2 throw exception for handle");
}
}).handle((retuanVal, throwable)->{
System.out.println("9.3 retuanVal: " + retuanVal);
System.out.println("9.4 throwable: " + throwable);
return "9.5 new return value.";
});
System.out.println("9.6 get: " + future9.get());
sleep(100);
}
private static void andTask() throws ExecutionException, InterruptedException {
// 10. thenCombine 合并结果
CompletableFuture<String> future10 = CompletableFuture.supplyAsync(()->{
sleep(100);
return "10.1 TaskA return value";
}).thenCombine(CompletableFuture.supplyAsync(()->{
sleep(100);
return "10.2 TaskB return value";
}), (taskAReturnVal, taskBReturnVal) -> taskAReturnVal + taskBReturnVal);
System.out.println("10.3 get: " + future10.get());
sleep(200);
// 11. thenAcceptBoth
CompletableFuture.supplyAsync(()->{
sleep(100);
return "11.1 TaskA return value";
}).thenAcceptBoth(CompletableFuture.supplyAsync(()->{
sleep(100);
return "11.2 TaskB return value";
}), (taskAReturnVal, taskBReturnVal) -> System.out.println(taskAReturnVal + taskBReturnVal));
sleep(200);
// 12. runAfterBoth A,B都执行完后才执行C,C不关心前面任务的返回值
CompletableFuture.supplyAsync(()->{
sleep(200); // 虽然这个任务先执行,但是执行时间比下面的任务长,所以最后会使用下面的返回结果
System.out.println("12.1 TaskA be called.");
return "12.2 TaskA return value";
}).runAfterBoth(CompletableFuture.supplyAsync(()->{
sleep(100);
System.out.println("12.3 TaskB be called.");
return "12.4 TaskB return value";
}), () -> System.out.println("12.5 TaskC be called."));
sleep(300);
}
private static void orTask() throws ExecutionException, InterruptedException {
// 13. applyToEither 使用A,B两个异步任务优先返回的结果
CompletableFuture<String> future13 = CompletableFuture.supplyAsync(()->{
sleep(200); // 虽然这个任务先执行,但是执行时间比下面的任务长,所以最后会使用下面的返回结果
System.out.println("13.1 TaskA be called"); // 用于证明拿到B的结果后,A还会继续执行,并不会终止
return "13.2 TaskA return value";
}).applyToEither(CompletableFuture.supplyAsync(()->{
sleep(100);
return "13.3 TaskB return value";
}), (returnVal) -> returnVal);
System.out.println("13.4 get: " + future13.get());
sleep(300);
// 14. acceptEither 使用A,B两个异步任务优先返回的结果
CompletableFuture.supplyAsync(()->{
sleep(200); // 虽然这个任务先执行,但是执行时间比下面的任务长,所以最后会使用下面的返回结果
return "14.1 TaskA return value";
}).acceptEither(CompletableFuture.supplyAsync(()->{
sleep(100);
return "14.2 TaskB return value";
}), (returnVal) -> System.out.println(returnVal));
sleep(300);
// 15. runAfterEither A,B任意一个执行完后就执行C,C不关心前面任务的返回值
CompletableFuture.supplyAsync(()->{
sleep(200); // 虽然这个任务先执行,但是执行时间比下面的任务长,所以最后会使用下面的返回结果
System.out.println("15.1 TaskA be called.");
return "15.2 TaskA return value";
}).runAfterEither(CompletableFuture.supplyAsync(()->{
sleep(100);
System.out.println("15.3 TaskB be called.");
return "15.4 TaskB return value";
}), () -> System.out.println("15.5 TaskC be called."));
sleep(300);
}
private static void complexTask() throws ExecutionException, InterruptedException {
// 16. anyOf
CompletableFuture future16 = CompletableFuture.anyOf(CompletableFuture.supplyAsync(()->
{
sleep(300);
System.out.println("16.1 TaskA be called.");
return "16.2 TaskA return value.";
}), CompletableFuture.supplyAsync(()->{
sleep(100);
System.out.println("16.3 TaskB be called.");
return "16.4 TaskB return value.";
}));
System.out.println("16.5 get: " + future16.get());
sleep(400);
// 17. allOf
CompletableFuture<Void> future17 = CompletableFuture.allOf(CompletableFuture.supplyAsync(()->
{
sleep(300);
System.out.println("17.1 TaskA be called.");
return "17.2 TaskA return value.";
}), CompletableFuture.supplyAsync(()->{
sleep(100);
System.out.println("17.3 TaskB be called.");
return "17.4 TaskB return value.";
}));
System.out.println("17.5 get: " + future17.get()); // allOf没有返回值
}
private static void sleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
}
}
}
复制代码
3. Summary
CompleteFeature supports complex asynchronous task scheduling, supports multiple tasks serial, parallel, and aggregation. When multiple asynchronous tasks have dependencies, scheduling tasks through CompleteFeature can greatly simplify code and improve execution performance.
end.