Introduction to concurrent programming in Java (19) Asynchronous task scheduling tool CompleteFeature

Java Geek | Author /   Kang Ran Yi Ye
This is the 60th original article by Java Geek

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:

  1. The task will use the first four methods without throwing an exception, otherwise use the last three methods.
  2. 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.
  3. 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.


<-Read the mark, like on the left !


Guess you like

Origin juejin.im/post/5e99c652518825737f1a7936