Java geek

Related reading:

Java concurrent Programming (1) Knowledge map Java concurrent programming (2) Atomic Java concurrent programming (3) Visibility Java Concurrent Programming (4) Sequential Java Concurrent Programming (5) Creation Of threads Overview Java Concurrent programming introduction (6) Synchronized usage Java Concurrent Programming (8) Thread Life Cycle (9) Java Concurrent programming (9) Deadlock and deadlock bits Java concurrent programming (10) Java concurrent programming (10) Lock optimization Java Concurrent Programming introduction (11) Stream limiting scenarios and Spring Stream Limiting Scenarios (12) Producer and Consumer patterns – Java concurrent Programming Introduction (13) Read/write locks and cache templates Java Concurrent Programming Introduction (14) CountDownLatch application scenarios CyclicBarrier: How to use Java concurrent Programming in seconds CyclicBarrier: How to Use Java Concurrent Programming Introduction to Java Concurrent Programming (20) Common locking scenarios and locking tools


1. CompleteFeature profile

The CompleteFeature is the enhancement of Feature. Feature can only handle simple asynchronous tasks, while the CompleteFeature can combine multiple asynchronous tasks in a complex way, supporting serial execution, parallel execution, And aggregation, Or aggregation, so that complex associated tasks can be scheduled.

2. Service scenarios supported by the CompleteFeature

2.1. Serial tasks

A serial task means that task B will not be executed until task A has completed its execution. A serial task has the following properties:

attribute describe
You get the result of A 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, the result of execution can be returned by the return value
You can obtain exception A Task B can obtain the exception thrown by task A
A If an exception occurs, the fault is terminated If so, 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:

methods You get the result of A B has a return value You can obtain exception A A If an exception occurs, the fault is terminated
thenRun no no no is
thenApply is is no is
thenAccept is no no is
thenCompose is is no is
whenComplete is no is no
exceptionally no is is no
handle is is is no

Conclusion:

  1. The first four methods are used if the task does not throw an exception, otherwise the last three methods are used.
  2. WhenComplete and handle are the catch and finall parts of try {} catch {} finally {}. The difference is that one has a return value. One has no return value.
  3. The difference between thenApply and thenCompose is that thenCompose returns a CompletableFuture in task B. See the following example to compare the difference.

1.2. And aggregation relationship

In the And aggregation relationship, task C is executed only after task A or task B is executed. The CompleteFeature supports this relationship in the following ways:

methods C receives the return value of A or B as an argument C has a return value
thenCombine is is
thenAcceptBoth is no
runAfterBoth no no

1.3. Or aggregation relationship

The Or aggregation relationship means that task C waits for the completion of either task A Or task B before executing, that is, C only needs to wait for the first completed task before executing. The CompleteFeature supports this relationship in the following ways:

methods C receives the return value of A or B as an argument C has a return value
applyToEither is is
acceptEither is no
runAfterEither no no

1.4 multitasking

CompletableFuture provides two methods for multitasking:

methods describe
anyOf When any one of the multiple tasks is completed, the task is finished, and the return value of the first completed task can be obtained.
allOf Multiple tasks are completed, and the return value of any task cannot be obtained

All of the above methods return a value of CompletableFuture, so you can continue to call the methods described earlier to combine the tasks to create a more complex task processing process.

1.5. Methods

The last task is executed in the same thread as the previous task, and the CompletableFuture also has a set of methods to execute the last task in a new thread, as long as the suffix Async is added to the original method. For example:

synchronous asynchronous
thenApply thenApplyAsync
thenAccept thenAcceptAsync
thenRun thenRunAsync
thenCompose thenComposeAsync

Specific what, can 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); // Wait for the child thread to finish
        System.out.println("end.");

    }

    private static void simpleTask(a) throws ExecutionException, InterruptedException {
        // 1. RunAsync executes an asynchronous task with no return value
        CompletableFuture.runAsync(()-> System.out.println("1. runAsync"));
        sleep(100);

        // 2. SupplyAsync executes an asynchronous task with a return value
        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(a) throws ExecutionException, InterruptedException {
        // 3. thenRun
        CompletableFuture.supplyAsync(()->{
            System.out.println("3.1 supplyAsync begin");
            sleep(100);  // To prove that B waits for A to finish before executing
            return "3.2 supplyAsync end";
        }).thenRun(()->{
            System.out.println("3.3 thenRun called.");
        });
        sleep(200);

        // 4. thenApply
        CompletableFuture<String> future4 = CompletableFuture.supplyAsync(()->{
            sleep(100);
            return "4.1 the apple";
        }).thenApply(returnVal->{
            return "4.2" + returnVal + "Apple";
        });
        System.out.println("4.3 the get." + future4.get());
        sleep(100);

        // 5. thenAccept
        CompletableFuture.supplyAsync(()->{
            sleep(100);
            return "5.1 orange";
        }).thenAccept(returnVal->{
            System.out.println("5.2" + returnVal + "- orange");
        });
        sleep(100);

        // 6. thenCompose
        CompletableFuture<String> future6 = CompletableFuture.supplyAsync(()->{
            sleep(100);
            return "6.1 the apple";
        }).thenCompose((returnVal)->{
            return CompletableFuture.supplyAsync(()->{
                return "6.2" + returnVal;
            });
        });
        System.out.println("6.3 the get." + future6.get());
        sleep(100);

        // 7. whenComplete
        CompletableFuture.supplyAsync(()->{
            sleep(100);
            if (true) {  // Modify the Boolean value to see different results
                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);  // You can get the return value directly without using future.get()
            System.out.println("7.3 throwable." + throwable);  // The asynchronous task throws an exception, and instead of terminating because of the exception, it goes here, and the rest of the code continues to execute
        });
        sleep(100);

        // 8. exceptionally
        CompletableFuture<String> future8 = CompletableFuture.supplyAsync(()->{
            sleep(100);
            if (false) {  // Modify the Boolean value to see different results
                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 the get." + future8.get());
        sleep(100);

        // 9. handle
        CompletableFuture<String> future9 = CompletableFuture.supplyAsync(()->{
            sleep(100);
            if (false) {  // Modify the Boolean value to see different results
                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 a value.";
        });
        System.out.println("9.6 the get." + future9.get());
        sleep(100);
    }

    private static void andTask(a) throws ExecutionException, InterruptedException {
        // 10. ThenCombine results
        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 the 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 and B are finished before C is executed. C does not care about the return value of the previous task
        CompletableFuture.supplyAsync(()->{
            sleep(200);  // Although this task is executed first, it takes longer to execute than the following tasks, so the following results will be used in the end
            System.out.println("12.1 TaskA called.");
            return "12.2 TaskA return value";
        }).runAfterBoth(CompletableFuture.supplyAsync(()->{
            sleep(100);
            System.out.println("12.3 TaskB called.");
            return "12.4 TaskB return value";
        }), () -> System.out.println("12.5 TaskC called."));
        sleep(300);
    }

    private static void orTask(a) throws ExecutionException, InterruptedException {
        // 13. ApplyToEither uses A and B asynchronous tasks to return results first
        CompletableFuture<String> future13 = CompletableFuture.supplyAsync(()->{
            sleep(200);  // Although this task is executed first, it takes longer to execute than the following tasks, so the following results will be used in the end
            System.out.println("13.1 TaskA be called"); // To prove that A will continue to execute after B's result is obtained
            return "13.2 TaskA return value";
        }).applyToEither(CompletableFuture.supplyAsync(()->{
            sleep(100);
            return "13.3 TaskB return value";
        }), (returnVal) -> returnVal);
        System.out.println("13.4 the get." + future13.get());
        sleep(300);

        // 14. An acceptEither will use the results returned by two asynchronous tasks A and B first
        CompletableFuture.supplyAsync(()->{
            sleep(200);  // Although this task is executed first, it takes longer to execute than the following tasks, so the following results will be used in the end
            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 to execute C, C does not care about the return value of the previous task
        CompletableFuture.supplyAsync(()->{
            sleep(200);  // Although this task is executed first, it takes longer to execute than the following tasks, so the following results will be used in the end
            System.out.println("15.1 TaskA called.");
            return "15.2 TaskA return value";
        }).runAfterEither(CompletableFuture.supplyAsync(()->{
            sleep(100);
            System.out.println("15.3 TaskB called.");
            return "15.4 TaskB return value";
        }), () -> System.out.println("15.5 TaskC called."));
        sleep(300);
    }

    private static void complexTask(a) throws ExecutionException, InterruptedException {
        // 16. anyOf
        CompletableFuture future16 = CompletableFuture.anyOf(CompletableFuture.supplyAsync(()->
        {
            sleep(300);
            System.out.println("16.1 TaskA called.");
            return "16.2 TaskA return a value.";
        }), CompletableFuture.supplyAsync(()->{
            sleep(100);
            System.out.println("16.3 TaskB called.");
            return "16.4 TaskB return a value.";
        }));
        System.out.println("16.5 the get." + future16.get());
        sleep(400);

        // 17. allOf
        CompletableFuture<Void> future17 = CompletableFuture.allOf(CompletableFuture.supplyAsync(()->
        {
            sleep(300);
            System.out.println("17.1 TaskA called.");
            return "17.2 TaskA return a value.";
        }), CompletableFuture.supplyAsync(()->{
            sleep(100);
            System.out.println("17.3 TaskB called.");
            return "17.4 TaskB return a value.";
        }));
        System.out.println("17.5 the get." + future17.get()); // allOf does not return a value
    }

    private static void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch(InterruptedException ie) { Thread.currentThread().interrupt(); }}}Copy the code

Output logs:

1. runAsync
2.3 after supplyAsync
2.1 supplyAsync task be called
2.2 supplyAsync return value
3.1 supplyAsync begin
3.4
3.5 xxx
3.6 AAA
3.3 thenRun be called.
4.3 get: 4.2 4.1Apple - apple5.2 5.1Orange, orange6.3 get: 6.2 6.1 apple
7.2 returnVal: 7.1 return value for whenComplete
7.3 throwable: null
java.util.concurrent.CompletionException: java.lang.RuntimeException: 8.2 throw exception for exceptionally
	at java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:273)
	at java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:280)
	at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1584)
	at java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1574)
	at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
	at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
	at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1689)
	at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
Caused by: java.lang.RuntimeException: 8.2 throw exception for exceptionally
	at com.javageektour.hikaricp.demo.CompleteFeatureDemo.lambda$serialTask$14(CompleteFeatureDemo.java:101)
	at com.javageektour.hikaricp.demo.CompleteFeatureDemo?Lambda$14/769287236.get(Unknown Source)
	at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1582)...5 more
8.4 get: 8.3 return value after dealing exception.
9.3 retuanVal: null
9.4 throwable: java.util.concurrent.CompletionException: java.lang.RuntimeException: 9.2 throw exception for handle
9.6 get: 9.5 new return value.
10.3 get: 10.1 TaskA returnValue10.2 TaskBreturn value
11.1 TaskA returnValue11.2 TaskBreturn value
12.3 TaskB be called.
12.1 TaskA be called.
12.5 TaskC be called.
13.4 get: 13.3 TaskB return value
13.1 TaskA be called
14.2 TaskB return value
15.3 TaskB be called.
15.5 TaskC be called.
15.1 TaskA be called.
16.3 TaskB be called.
16.5 get: 16.4 TaskB return value.
16.1 TaskA be called.
17.3 TaskB be called.
17.1 TaskA be called.
17.5 get: null
end.
Copy the code

3. Summary

CompleteFeature supports complex asynchronous task scheduling and supports multiple serial, parallel and aggregated tasks. When multiple asynchronous tasks have a dependency, scheduling tasks through CompleteFeature can greatly simplify code and improve execution performance.

end.


<– read left mark, left point like!