SoFunction
Updated on 2025-04-14

springboot CompletableFuture asynchronous thread pool detailed explanation

4 ways to initialize asynchronous

1. Inherit Thread

2. Implement Runnable

3. Implement Callable interface + Future (can get the return result, and can handle exceptions after jdk1.5)

4. Thread pool [ExecutorService] (used in actual development)

  • Methods 1 and 2: The main thread cannot get the thread's operation result.
  • Method 3: The main thread can obtain the thread's operation results, but it is not conducive to controlling thread resources in the server. Can cause server resources to run out.
  • Method 4: The thread pool has stable performance, controls resources, and can also obtain execution results and catch exceptions.

Seven major parameters of thread pool

  1. corePoolSize: [5] Number of core threads [always exist unless (allowCoreThreadTimeOut will be recycled)]; thread pool, 5 threads are prepared after creation Thread thread = new Thread(); and it has not started. Only after submitting tasks to the thread pool will it be executed ();
  2. maximumPoolSize: [200] Maximum number of threads; control resources
  3. keepAliveTime: Survival time. If the current number of threads is greater than the number of cores. Frees the free thread (maximumPoolSize-corePoolSize). As long as the thread is idle is greater than the specified keepAliveTime.
  4. unit: time unit.
  5. BlockingQueue workQueue: Blocks the queue. If there are many tasks, the current many tasks will be placed in the queue. As long as there are threads free, go back to the queue to retrieve the new task and continue to execute.
    new LinedBlockingDeque<>(): The default is the maximum value of Integer, which will cause insufficient memory
  6. threadFactory: thread creation factory
  7. RejectedExecutionHandler handler: Rejection kills the column, if the queue is full, executes the corresponding rejection policy.
    7.1 DiscardOldestPolicy: When a new task comes in, discard the old task that has not been executed
    7.2 CallerRunsPolicy: Directly call the run method to execute synchronously
    7.3 AbortPolicy: directly discard new tasks and throw exceptions
    7.4 DiscardPolicy: Discard directly and not throw exceptions

Work order

  1. Thread pool creation, core number of core threads ready to accept tasks
  2. When the core is full, the incoming tasks will be placed in the blocking queue, and the idle core will block the queue and obtain task execution by itself.
  3. When the blocking queue is full, a new thread will be opened directly to execute, and the maximum number can be opened to the specified number of max.
  4. When max is full, use the RejectedExecutionHandler policy to reject the task.
  5. All max is executed and there is a lot of free time. After keepAliveTime at the specified time, release these threads such as max-core.

Executors

  1. newCachedThreadPool() core is 0, all can be recycled
  2. newFixedThreadPool() fixed size core= max; none of them can be recycled
  3. newScheduledThreadPllo() thread pool for timing tasks
  4. newSingleThreadExecutor() is a single threaded thread pool, and the background gets tasks to be executed one by one.

1. Create an asynchronous object

CompleteFuture provides four static methods

//You can get the return value and pass it into a custom thread poolpublic static &lt;U&gt; CompletableFuture&lt;U&gt; supplyAsync(Supplier&lt;U&gt; supplier)
public static &lt;U&gt; CompletableFuture&lt;U&gt; supplyAsync(Supplier&lt;U&gt; supplier, Executor executor)
//No return value, can be passed into a custom thread poolpublic static CompletableFuture&lt;Void&gt; runAsync(Runnable runnable)
public static CompletableFuture&lt;Void&gt; runAsync(Runnable runnable,Executor executor)

test

public static ExecutorService service = (10);
  //No return value        (()-&gt;{
            ("Async task was successfully completed");
        },service);
        //Empty parameter only returns value        CompletableFuture&lt;String&gt; stringCompletableFuture = (() -&gt; {
            return "Return Value";
        }, service);

2. Callback method when calculation is completed

//The previous task is completed and the previous task is used in the same threadpublic CompletableFuture&lt;T&gt; whenComplete(BiConsumer&lt;? super T, ? super Throwable&gt; action);
//Leave it to the thread pool to restart a threadpublic CompletableFuture&lt;T&gt; whenCompleteAsync(BiConsumer&lt;? super T, ? super Throwable&gt; action);
//After the task is completed (only perceived)public CompletableFuture&lt;T&gt; whenComplete(BiConsumer&lt;? super T, ? super Throwable&gt; action);
//Responsive exceptions at the same time modify the return valuepublic CompletableFuture&lt;T&gt; exceptionally(Function&lt;Throwable, ? extends T&gt; fn);

Test code

 CompletableFuture&lt;Integer&gt; future = (() -&gt; {
            return 10;
        }, service).whenComplete((res,excption)-&gt;{
            //Although you can get exception message, you cannot modify and return result            ("Async task was successfully completed"+res+"Or exception:"+excption);
        }).exceptionally(throwable -&gt;{
            // Handle exceptions and can modify data return value            return 0;
       });

3. Handle method (processed and returned when exception)

//Execute with the previous task using the same threadpublic &lt;U&gt; CompletableFuture&lt;U&gt; handle(BiFunction&lt;? super T, Throwable, ? extends U&gt; fn)
//The default thread pool enables thread executionpublic &lt;U&gt; CompletableFuture&lt;U&gt; handleAsync(BiFunction&lt;? super T, Throwable, ? extends U&gt; fn)
//Specify your own thread pool to enable thread executionpublic &lt;U&gt; CompletableFuture&lt;U&gt; handleAsync(BiFunction&lt;? super T, Throwable, ? extends U&gt; fn, Executor executor)

4. Handle test

       //The processing after the method execution is completed is successful or failed        CompletableFuture&lt;Integer&gt; future = (() -&gt; {
            return 10;
        }, service).handle((res,thr)-&gt;{
            //No exceptions appear, of course, you can not write them here for concealment           if(res!=null){
               return 0;
           }
           //Exception occurred           if(thr!=null){
               return 1;
           }
           return 0;
        });

5. Thread serialization method (Task B needs the execution result of Task A before it can be executed)

//A-->B-->C Perceive the previous result and return the last resultpublic &lt;U&gt; CompletableFuture&lt;U&gt; thenApply(Function&lt;? super T,? extends U&gt; fn)
public &lt;U&gt; CompletableFuture&lt;U&gt; thenApplyAsync(Function&lt;? super T,? extends U&gt; fn)
public &lt;U&gt; CompletableFuture&lt;U&gt; thenApplyAsync(Function&lt;? super T,? extends U&gt; fn,Executor executor)

//B can sense the return value of Apublic CompletableFuture&lt;Void&gt; thenAccept(Consumer&lt;? super T&gt; action)
public CompletableFuture&lt;Void&gt; thenAcceptAsync(Consumer&lt;? super T&gt; action) 
public CompletableFuture&lt;Void&gt; thenAcceptAsync(Consumer&lt;? super T&gt; action,
                                                   Executor executor)
//A->After completing (Async starts a new thread, but does not have the same thread as A) cannot sense the execution result of the previous steppublic CompletableFuture&lt;Void&gt; thenRun(Runnable action) 
public CompletableFuture&lt;Void&gt; thenRunAsync(Runnable action) 
public CompletableFuture&lt;Void&gt; thenRunAsync(Runnable action, Executor executor) 

test

CompletableFuture&lt;Void&gt; voidCompletableFuture = (() -&gt; {
            //Task A            return 10;
        }, service).thenRunAsync(() -&gt; {
            //The result of task A cannot be sensed        },service);
CompletableFuture&lt;Void&gt; voidCompletableFuture = (() -&gt; {
            //Task A            return 10;
        }, service).thenAcceptAsync((res) -&gt; {
            //The result of task A can be sensed, but the data cannot be returned            int i = res / 2;
        },service);
CompletableFuture&lt;Integer&gt; future = (() -&gt; {
            //Task A            return 10;
        }, service).thenApplyAsync((res) -&gt; {
        //The return value shall prevail the last return            return 10 + res;
        }, service);

6. Two tasks combinations - both must be completed

The following three ways Both tasks must be completed,This task is triggered
//Combining two futures, obtaining the return result of the two future tasks, and returning the return value of the current taskpublic &lt;U,V&gt; CompletableFuture&lt;V&gt; thenCombine(
        CompletionStage&lt;? extends U&gt; other,
        BiFunction&lt;? super T,? super U,? extends V&gt; fn)

public &lt;U,V&gt; CompletableFuture&lt;V&gt; thenCombineAsync(
        CompletionStage&lt;? extends U&gt; other,
        BiFunction&lt;? super T,? super U,? extends V&gt; fn)

public &lt;U,V&gt; CompletableFuture&lt;V&gt; thenCombineAsync(
        CompletionStage&lt;? extends U&gt; other,
        BiFunction&lt;? super T,? super U,? extends V&gt; fn, Executor executor) 

//Combining two futures, getting the return results of the two future tasks, and then processing the task, without a return value.public &lt;U&gt; CompletableFuture&lt;Void&gt; thenAcceptBoth(
        CompletionStage&lt;? extends U&gt; other,
        BiConsumer&lt;? super T, ? super U&gt; action)

public &lt;U&gt; CompletableFuture&lt;Void&gt; thenAcceptBothAsync(
        CompletionStage&lt;? extends U&gt; other,
        BiConsumer&lt;? super T, ? super U&gt; action)
        
public &lt;U&gt; CompletableFuture&lt;Void&gt; thenAcceptBothAsync(
        CompletionStage&lt;? extends U&gt; other,
        BiConsumer&lt;? super T, ? super U&gt; action, Executor executor) 
        
//Combining two futures does not need to obtain the result of futures, only two futures are processed after completing the taskpublic CompletableFuture&lt;Void&gt; runAfterBoth(CompletionStage&lt;?&gt; other,Runnable action) 

public CompletableFuture&lt;Void&gt; runAfterBothAsync(CompletionStage&lt;?&gt; other,Runnable action) 
                                                     
public CompletableFuture&lt;Void&gt; runAfterBothAsync(CompletionStage&lt;?&gt; other,Runnable action,Executor executor)

test

        CompletableFuture&lt;Integer&gt; future01 = (() -&gt; {
            //Task A            (().getId());
            ("As soon as the mission ends");
            return 10/2;
        }, service);
        CompletableFuture&lt;String&gt; future02 = (() -&gt; {
            //Task A            (().getId());
            ("Task 2 ends");
            return "future02";
        }, service);
        //The results cannot be sensed        CompletableFuture&lt;Void&gt; future03void = (future02, () -&gt; {
            ("Task Three Execution End");
        }, service);
        //You can sense and obtain the results of the first two tasks        CompletableFuture&lt;Void&gt; future03No = (future02, (res1, res2) -&gt; {
            ("Task Three Execution End");
        }, service);
        CompletableFuture&lt;String&gt; future03 = (future02, (res1, res2) -&gt; {
            ("Task Three Execution End");
            return res1 + "-" + res2;
        }, service);

7. Complete two tasks in one

//One of the two tasks has been completed, get its return value, process the task and have a new return valuepublic &lt;U&gt; CompletableFuture&lt;U&gt; applyToEither( CompletionStage&lt;? extends T&gt; other, Function&lt;? super T, U&gt; fn) 

public &lt;U&gt; CompletableFuture&lt;U&gt; applyToEitherAsync(CompletionStage&lt;? extends T&gt; other, Function&lt;? super T, U&gt; fn)
        
public &lt;U&gt; CompletableFuture&lt;U&gt; applyToEitherAsync(CompletionStage&lt;? extends T&gt;other,Function&lt;? super T, U&gt; fn,Executor executor) 
//One of the two tasks has been completed, get its return value, process the task, and there is no new return value.public CompletableFuture&lt;Void&gt; acceptEither(CompletionStage&lt;? extends T&gt; other, Consumer&lt;? super T&gt; action)

public CompletableFuture&lt;Void&gt; acceptEitherAsync(CompletionStage&lt;? extends T&gt; other,Consumer&lt;? super T&gt; action)

public CompletableFuture&lt;Void&gt; acceptEitherAsync(CompletionStage&lt;? extends T&gt; other,Consumer&lt;? super T&gt; action,Executor executor)
//One of the two tasks has been completed, and there is no need to get the result of future, the task is processed, and no value is returned.public CompletableFuture&lt;Void&gt; runAfterEither(CompletionStage&lt;?&gt; other,Runnable action)

public CompletableFuture&lt;Void&gt; runAfterEitherAsync(CompletionStage&lt;?&gt; other,Runnable action)
                                                       
public CompletableFuture&lt;Void&gt; runAfterEitherAsync(CompletionStage&lt;?&gt; other,Runnable action,Executor executor)

test

		 /**
          * If one of two tasks is completed, then three will be executed.
          *
          */
CompletableFuture&lt;Object&gt; future01 = (() -&gt; {
            //Task A            try {
                (3000);
            } catch (InterruptedException e) {
                ();
            }
            (().getId());
            ("As soon as the mission ends");

            return 10/2;
        }, service);
        CompletableFuture&lt;Object&gt; future02 = (() -&gt; {
            //Task A            (().getId());

            ("Task 2 ends");
            return "future02";
        }, service);
        //Don't perceive the result, and I don't return the value either        (future02,()-&gt;{
            ("Task Three Execution End");
        },service);
        //Sense the result, and it does not return the value        (future02,(res)-&gt;{
            //Sense the result that the thread has processed and completed            ("Task Three Execution End"+res);
        },service);
        //See the result and return your own return value        CompletableFuture&lt;String&gt; stringCompletableFuture = (future02, res -&gt; {
            return "Task Three Results" + res;
        }, service);

8. Multi-task combination

//Waiting for all tasks to completepublic static CompletableFuture&lt;Void&gt; allOf(CompletableFuture&lt;?&gt;... cfs)
//As long as there is a task completedpublic static CompletableFuture&lt;Object&gt; anyOf(CompletableFuture&lt;?&gt;... cfs)

test

CompletableFuture&lt;Object&gt; future01 = (() -&gt; {
            //Task A            (().getId());
            ("As soon as the mission ends");

            return 10/2;
        }, service);
        CompletableFuture&lt;Object&gt; future02 = (() -&gt; {
            //Task A            (().getId());
            try {
                (3000);
            } catch (InterruptedException e) {
                ();
            }
            ("Task 2 ends");
            return "future02";
        }, service);
        //This method is blocking waiting and is not recommended to use it here        //();
        //();
        
		//Waiting for all tasks to complete will not block        CompletableFuture&lt;Void&gt; allOf= (future01, future02);
        //Waiting for all to be completed (two tasks are executed at the same time)        ();
        ("end");
        (()+""+());
        //Only one completes        CompletableFuture&lt;Object&gt; anyOf= (future01, future02);
        ();
        (());

Summarize

The above is personal experience. I hope you can give you a reference and I hope you can support me more.