Future & completefuture practice summary

Time:2022-5-28

1 future introduction

1.1 main functions of future

Jdk5 adds a future interface to describe the results of an asynchronous calculation.

Future is to cancel the execution result of a specific runnable or callable task, query whether it is completed, and obtain the result.If necessary, you can get the execution result through the get method, which will block until the task returns the result.

The future class is located in java Util Under the concurrent package, it is an interface:

public interface Future<V> { 
 /** 
  *Method is used to cancel a task. It returns true if the task is cancelled successfully, and false if the task is cancelled unsuccessfully* 
  *@param mayinterruptifrunning indicates whether to cancel tasks that are being executed but have not been completed. If true is set, it indicates that tasks in the process of execution can be canceled. 
  *@return if the task has been completed, whether mayinterruptifrunning is true or false, this method will definitely return false, that is, if you cancel the completed task, it will return false; 
  *If the task is executing, return true if mayinterruptifrunning is set to true, and false if mayinterruptifrunning is set to false; 
  *If the task has not been executed, it must return true whether mayinterruptifrunning is true or false. 
  */ 
  boolean cancel(boolean mayInterruptIfRunning); 

   /** 
    *Method indicates whether the task was successfully canceled 
    *@return returns true if the task is successfully cancelled before it is completed normally 
    */ 
   boolean isCancelled(); 

   /** 
    *Method indicates whether the task has been completed 
    *@return returns true if the task is completed 
    */ 
   boolean isDone(); 

   /** 
    *Method is used to obtain the execution result. This method will cause blocking and will not return until the task is completed 
    *@return result value of task execution 
    *@throws interruptedexception thread interrupted exception
    *@throws executionexception task execution exception. If the task is canceled, a cancellationexception will be thrown
    */ 
   V get() throws InterruptedException, ExecutionException; 

   /** 
    *It is used to obtain the execution result. If the result is not obtained within the specified time, it will directly return null (it is not an exception thrown, which should be noted). 
    *@param timeout timeout timeout 
    *@param unit timeout unit 
    * @return 
    * @throws InterruptedException 
    * @throws ExecutionException 
    *@throws timeoutexception if the calculation times out, a timeoutexception will be thrown (to be confirmed)
    */ 
   V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; 
}

As can be seen from the above method comments, futrue provides three functions:

1) Judge whether the task is completed;

2) Ability to interrupt tasks;

3) Be able to obtain task execution results. (most commonly used)

1.2 limitations of future

In essence, future represents the result of an asynchronous computation. It provides isdone () to detect whether the calculation has been completed, and after the calculation, you can obtain the calculation result through the get () method. In asynchronous computing, future is really an excellent interface. However, it does have many limitations:

  • Concurrent multitasking: future only provides the get () method to get the results, and it is blocked. Therefore, there is no alternative but to wait for you; When the for loop obtains future results in batches, it is easy to block, so the timeout limit should be used when calling the get method.
  • Cannot chain call multiple tasks: if you want to perform specific actions after the calculation task is completed, such as sending e-mail, but future does not provide such capabilities;
  • Cannot combine multiple tasks: if you run 10 tasks and expect to perform specific actions after all of them are executed, there is nothing you can do in future;
  • No exception handling: there is no exception handling method in the future interface;

2 Introduction to completabilefuture

Although future and related usage methods provide the ability to execute tasks asynchronously, it is very inconvenient to obtain the results. The results of tasks can only be obtained by blocking or polling. If the execution of the previous task is slow, you need to block and wait for the previous task to complete the execution of the next task before you can get results.

The blocking method is obviously contrary to the original intention of our asynchronous programming. The polling method will consume unnecessary CPU resources and can not get the calculation results in time. The main function of completabilefuture is to obtain the return value of the task while generating the task. Let the two things be executed separately, and the tasks will not block each other. You can achieve the first result after the first execution, and no longer rely on the task order.

2.1 completabilefuture principle

Internally, the blocking queue +futuretask enables tasks to be completed first and obtained first, that is, the results are sorted according to the completion order,There is a first in first out blocking queue inside, which is used to save the future that has been executed. A future that has been executed can be obtained by calling its take method or poll method, and then the final result can be obtained by calling the get method of the future interface implementation class.

2.2 application scenarios

When asynchronous tasks need to be submitted in batches, it is recommended to use completabilefuture. Completabilefuture integrates the functions of thread pool executor and blocking queue BlockingQueue to make the management of batch asynchronous tasks easier.

Completabilefuture can make the execution results of asynchronous tasks orderly. The first execution is entered into the blocking queue first. With this feature, you can easily achieve the order of subsequent processing and avoid unnecessary waiting.

Thread pool isolation. Completionservice supports creating thread pools by itself. This isolation performance avoids the risk that several particularly time-consuming tasks will bring down the entire application.

2.3 use details of completabilefuture

For simple tasks, it’s OK to use future to obtain results, but the multiple asynchronous tasks we submit in parallel are often not independent,In many cases, business logic processing involves serial [dependency], parallel, and aggregation. It is very troublesome for us to implement it manually with furniture.

Completabilefuture is an extension and enhancement of the future interface. Completabilefuture implements the future interface and makes rich extensions on this basis, which perfectly makes up for the above problems of future.

More importantly,Completabilefuture implements the ability to arrange tasks. With this capability, we can easily organize the running sequence, rules and methods of different tasks.To some extent, this ability is its core ability. In the past, although tasks can be arranged through tool classes such as countdownlatch, complex logic processing is required, which is not only energy-consuming but also difficult to maintain.

3. Application of completabilefuture

Future & completefuture practice summary

Classification by function

Future & completefuture practice summary

Classification by application scenario
  • On many methods, the thread pool can be specified, while the method without the executor will use forkjoinpool Commonpool () executes asynchronous code as its thread pool. If a thread pool is specified, it runs with the specified thread pool.

  • By default, completable future will use the public forkjoinpool thread pool. The number of threads created by this thread pool by default is the number of cores of the CPU (you can also set the number of threads in the forkjoinpool thread pool through JVM option:-djava.util.concurrent.forkjoinpool.common.parallel).

  • If all completabilefuture shares a thread pool, once a task performs some slow i/o operations, all threads in the thread pool will be blocked on the i/o operations, resulting in thread starvation, which will affect the performance of the whole system. Therefore, it is strongly recommended to create different thread pools according to different business types to avoid mutual interference.

  • When we use it, we will noticeCompletableFutureMethod naming rules for:

    • xxx(): indicates that the method will continue to execute in the existing thread;
  • xxxAsync(): indicates that other threads may be used for execution (if the same thread pool is used, the same thread may also be selected for execution).

4 use cases

4.1 basic use cases

Serial execution:

Define twoCompletableFuture, firstCompletableFutureQuery the securities code according to the securities name, the secondCompletableFutureQuery the securities price according to the securities code. These twoCompletableFutureThe serial operation is realized as follows:

Completabilefuture Supplyasync(): create an asynchronous task containing the return value;
Thenapplyasync(): get the result of the previous thread for conversion, with a return value;
Thenaccept(): get the result of the previous thread for consumption, and there is no return value.
public class Demo {
    private static CountDownLatch countDownLatch = new CountDownLatch(1);

    public static void main(String[] args) throws Exception {
        //First task: create a completabilefuture containing the return value
        CompletableFuture<String> cfQuery = CompletableFuture.supplyAsync(() -> {
            Return querycode ("PetroChina");
        });
        //CFQUERY succeeds. Continue to the next task:
        CompletableFuture<Double> cfFetch = cfQuery.thenApplyAsync((code) -> {
            return fetchPrice(code);
        });
        //Cfthe result is printed after the fetch succeeds:
        cfFetch.thenAccept((result) -> {
            System.out.println("price: " + result);
        });
        //Do not end the main thread, or the thread pool used by completabilefuture by default will be closed immediately:
        countDownLatch.await();
    }

    public static void main2(String[] args) throws Exception {
        Completabilefuture Supplyasync (() - > querycode ("PetroChina")
                .thenApplyAsync((code) -> fetchPrice(code))
                .thenAccept((result) -> System.out.println("price: " + result));
        countDownLatch.await();
    }

    static String queryCode(String name) {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
        }
        String code = "601857";
        System Out Println ("query securities code, name:" + name + ", code:" + code ");
        return code;
    }

    static Double fetchPrice(String code) {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
        }
        Double price = 5 + Math.random() * 20;
        System Out Println ("query the price according to the securities code, code:" + code + "; price:" + price ");
        return price;
    }
}

Parallel execution:

In addition to serial execution, multipleCompletableFutureIt can also be executed in parallel. For example, we consider such a scenario:

At the same time, you can query the securities code from Sina and NetEase. As long as any one returns the result, you can query the price in the next step. As long as any one returns the result, you can complete the operation:

Completabilefuture Supplyasync(): create an asynchronous task containing the return value;
Completabilefuture Anyof (CF1, CF2, CF3) Join(): multiple asynchronous threads return after any execution, with the return value object;
Thenaccept(): get the result of the previous thread for consumption, and there is no return value.
public class Demo2 {
    private static CountDownLatch COUNT_DOWN_LATCH = new CountDownLatch(1);

    public static void main(String[] args) throws Exception {
        //Two completabilefuture execute asynchronous queries:
        CompletableFuture<String> cfQueryFromSina = CompletableFuture.supplyAsync(() -> {
            Return querycode ("PetroChina"“ https://finance.sina.com.cn/code/ ");
        });
        CompletableFuture<String> cfQueryFrom163 = CompletableFuture.supplyAsync(() -> {
            Return querycode ("PetroChina"“ https://money.163.com/code/ ");
        });

        //Merge anyof into a new completabilefuture:
        CompletableFuture<Object> cfQuery = CompletableFuture.anyOf(cfQueryFromSina, cfQueryFrom163);

        //Two completabilefuture execute asynchronous queries:
        CompletableFuture<Double> cfFetchFromSina = cfQuery.thenApplyAsync((code) -> {
            return fetchPrice((String) code, "https://finance.sina.com.cn/price/");
        });
        CompletableFuture<Double> cfFetchFrom163 = cfQuery.thenApplyAsync((code) -> {
            return fetchPrice((String) code, "https://money.163.com/price/");
        });

        //Merge anyof into a new completabilefuture:
        CompletableFuture<Object> cfFetch = CompletableFuture.anyOf(cfFetchFromSina, cfFetchFrom163);

        //Final result:
        cfFetch.thenAccept((result) -> {
            System.out.println("price: " + result);
        });
        //Do not end the main thread immediately, or the thread pool used by completabilefuture by default will be closed immediately:
        COUNT_DOWN_LATCH.await();
    }

    public static void main2(String[] args) throws Exception {
        //Two completabilefuture execute asynchronous queries:
        Completabilefuture<string> cfqueryfromsina = completabilefuture Supplyasync (() - > querycode ("PetroChina"“ https://finance.sina.com.cn/code/ "));
        Completabilefuture<string> cfqueryfrom163 = completabilefuture Supplyasync (() - > querycode ("PetroChina"“ https://money.163.com/code/ "));

        //Merge anyof into a new completabilefuture:
        CompletableFuture<Object> cfQuery = CompletableFuture.anyOf(cfQueryFromSina, cfQueryFrom163);

        //Two completabilefuture execute asynchronous queries:
        CompletableFuture<Double> cfFetchFromSina = cfQuery.thenApplyAsync((code) -> fetchPrice((String) code, "https://finance.sina.com.cn/price/"));
        CompletableFuture<Double> cfFetchFrom163 = cfQuery.thenApplyAsync((code) -> fetchPrice((String) code, "https://money.163.com/price/"));

        //Merge anyof into a new completabilefuture:
        CompletableFuture<Object> cfFetch = CompletableFuture.anyOf(cfFetchFromSina, cfFetchFrom163);

        //Final result:
        cfFetch.thenAccept((result) -> System.out.println("price: " + result));
        //Do not end the main thread immediately, or the thread pool used by completabilefuture by default will be closed immediately:
        COUNT_DOWN_LATCH.await();
    }

    static String queryCode(String name, String url) {
        System.out.println(Thread.currentThread().getName() + " query code from " + url + "...");
        try {
            Thread.sleep((long) (Math.random() * 100));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        String code = "601857";
        System Out Println (thread. Currentthread() Getname() + "query securities code, name:" + name + ", code:" + code ";
        return code;
    }

    static Double fetchPrice(String code, String url) {
        System.out.println(Thread.currentThread().getName() + " query price from " + url + "...");
        try {
            Thread.sleep((long) (Math.random() * 100));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Double price = 5 + Math.random() * 20;
        System Out Println (thread. Currentthread() Getname() + "query the price according to the securities code, code:" + code + "; price:" + price ";
        return price;
    }
}

The asynchronous query rules implemented by the above logic are actually:

Future & completefuture practice summary

4.2 realize the optimal “boiling water and making tea” program

Future & completefuture practice summary

public class Demo3 {

    public static void main(String[] args) {

        //Task 1: washing kettle - > boiling water
        CompletableFuture<String> f11 = CompletableFuture.supplyAsync(() -> {
            System Out Println ("t1: kettle... Start");
            sleep(1000);
            Return "t1: kettle... Done";
        });
        CompletableFuture<String> f12 = f11.thenApply((f11Result) -> {
            System.out.println(f11Result);
            System Out Println ("t1: boiling water... Start");
            sleep(3000);
            Return "t1: boil water... Done";
        });

        //Task 2: wash the teapot - > wash the teacup - > get the tea
        CompletableFuture<Void> f21 = CompletableFuture.runAsync(() -> {
            System Out Println ("=============================t2: teapot washing... Start");
            sleep(1000);
            System Out Println ("============================t2: teapot washing... Completed");
        });
        CompletableFuture<Void> f22 = f21.thenRun(() -> {
            System Out Println ("============================t2: tea cup washing... Start");
            sleep(2000);
            System Out Println ("============================t2: tea cup washing... Completed");
        });
        CompletableFuture<String> f23 = f22.thenApply(result -> {
            System Out Println ("============================t2: take tea... Start");
            sleep(1000);
            System Out Println ("============================t2: take tea... Complete");
            Return "Longjing";
        });
        //Task 3: execute after task 1 and task 2 are completed: make tea
        CompletableFuture<String> f3 = f12.thenCombine(f23, (f1Result, f2Result) -> {
            System.out.println(f1Result);
            System Out Println ("****************t2: get tea: result" + f2result);
            System Out Println ("**************** t3: making tea..., what tea:" + f2result);
            Return "serve tea:" + f2result;
        });
        //Wait for task 3 execution result
        System.out.println(f3.join());
    }

    static void sleep(int t) {
        try {
            Thread.sleep(t);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Reference documents:

CompletableFuture (Java Platform SE 8 )

Future & completefuture analysis practice