Explanation of completable future asynchronous and thread pool

1, Thread review

1. Four ways to initialize threads
1) , inherit Thread
2) . implement Runnable interface
3) . implement Callable interface + FutureTask (you can get the returned results and handle exceptions)
4) Thread pool
Methods 1 and 2: the main process cannot obtain the operation result of the thread. Not suitable for the current scene
Method 3: the main process can obtain the operation results of the thread, but it is not conducive to controlling the thread resources in the server. Can lead to
The server is out of resources.
Method 4: initialize the thread pool in the following two ways
Executors.newFiexedThreadPool(3);
//Or
new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit unit,
workQueue, threadFactory, handler);
With stable thread pool performance, you can also obtain execution results and catch exceptions. However, in the case of complex business, one
An asynchronous call may depend on the execution result of another asynchronous call.
 
2. Seven parameters of thread pool
corePoolSize: the number of threads that remain in the pool, even if the threads are idle. Unless allowCoreThreadTimeOut is set
maximumPoolSize: the maximum number of threads allowed in the pool
keepAliveTime: when the number of threads is greater than the number of core threads, the maximum length of time a thread does not receive a new task will terminate the release,
Finally, the thread pool is maintained at the size of corePoolSize
Unit: time unit
workQueue: a blocking queue used to store tasks waiting to be executed. If the current demand for threads exceeds the corePoolSize
Size, it will be placed here waiting for the idle thread to execute.
threadFactory: a factory that creates threads, such as specifying thread names
handler: reject policy. If the thread is full, the thread pool will use the reject policy.

 

 

Operation process:
1. Create a thread pool, prepare the number of core threads, and prepare to accept tasks
2. New tasks come in and are executed with idle threads prepared by the core.
(1) When the core is full, put the incoming tasks into the blocking queue. The idle core will block the team by itself
Column get task execution
(2) When the blocking queue is full, a new thread is directly opened for execution. The maximum number can only be opened to the number specified by max
(3) And max have been implemented. The number of max core idle threads will start running after the time specified by keepAliveTime
Dynamic destruction. Finally, keep to the core size
(4) If the number of threads reaches max and new tasks come in, the reject policy specified by reject will be used
Process slightly
3. All threads are created by the specified factory.
 
3. Four common thread pools
newCachedThreadPool
Create a cacheable thread pool. If the length of the thread pool exceeds the processing needs, you can flexibly recycle idle threads. If there is no recyclable thread, you can create a new thread.
 
newFixedThreadPool
Create a fixed length thread pool to control the maximum concurrent number of threads. The exceeded threads will wait in the queue.
 
newScheduledThreadPool
Create a fixed length routing pool to support regular and periodic task execution.
 
newSingleThreadExecutor
Create a singleton thread pool. It will only use a unique worker thread to execute tasks to ensure that all tasks are executed in the specified order (FIFO, LIFO, priority).
 
4. Why use thread pool in development
 
Reduce resource consumption
 
Reduce the loss caused by thread creation and destruction by reusing the created threads
 
Improve response speed
 
Because when the number of threads in the thread pool does not exceed the maximum limit of the thread pool, some threads are waiting to allocate tasks. When tasks come, they can be executed without creating new threads
 
Improve thread manageability
 
The thread pool will optimize the threads in the pool according to the current system characteristics to reduce the system overhead caused by creating and destroying threads. Unlimited thread creation and destruction not only consume system resources, but also reduce the stability of the system. Thread pool is used for unified allocation
 

2, Completable future asynchronous orchestration

Business scenario:
The logic of querying the product details page is complex. Some data needs to be called remotely, which will inevitably take more time.

If each query on the product details page needs to be completed within the time indicated below

Then, users need 5.5s to see the content of the product details page. Obviously, it is unacceptable.
If multiple threads complete these six steps at the same time, it may take only 1.5s to complete the response.
 
Future is a class added in Java 5 to describe the results of an asynchronous calculation. You can use the 'isDone' method to check whether the calculation is completed, or use the 'get' method to block the calling thread until the calculation is completed and the result is returned. You can also use the 'cancel' method to stop the execution of the task.
 
Completabilefuture and FutureTask belong to the implementation classes of the Future interface, and can obtain the execution results of threads.

 

 

1. Create asynchronous object
Completable future provides four static methods to create an asynchronous operation.
 
runXxxx does not return results, and supplyxx can obtain the returned results
You can pass in a custom thread pool, otherwise the default thread pool will be used;
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
        System.out.println("Current thread:" + Thread.currentThread().getId());
        int i = 10 / 2;
        System.out.println("Operation results:" + i);
        }, executor);
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
        System.out.println("Current thread:" + Thread.currentThread().getId());
        int i = 10 / 0;
        System.out.println("Operation results:" + i);
             return i;
         }, executor);
Integer result = future.get();
 
2. Callback method when calculation is completed
whenComplete can handle normal and abnormal calculation results, and exceptionally handle abnormal situations.
The difference between whenComplete and whenCompleteAsync:
whenComplete: the thread executing the current task executes the task that continues to execute whenComplete.
whenCompleteAsync: the task of submitting whenCompleteAsync to the thread pool
To execute.
The method does not end with Async, which means that the Action is executed with the same thread, and Async may use other threads
Execution (if the same thread pool is used, it may also be selected for execution by the same thread)
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
             System.out.println("Current thread:" + Thread.currentThread().getId());
             int i = 10 / 0;
             System.out.println("Operation results:" + i);
             return i;
         }, executor).whenComplete((res,exception) -> {
             //Although the exception information can be obtained, the returned data cannot be modified
             System.out.println("The asynchronous task completed successfully...The result is:" + res + "The exception is:" + exception);
         }).exceptionally(throwable -> {
             //It can sense the exception and return the default value
             return 10;
         });

 

3. handle method

Like complete, the result can be finally processed (exceptions can be handled) and the return value can be changed.

/**
         * Method finished performing back-end processing
         */
         CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
             System.out.println("Current thread:" + Thread.currentThread().getId());
             int i = 10 / 2;
             System.out.println("Operation results:" + i);
             return i;
         }, executor).handle((result,thr) -> {
             if (result != null) {
                 return result * 2;
             }
             if (thr != null) {
                 System.out.println("The asynchronous task completed successfully...The result is:" + result + "The exception is:" + thr);
                 return 0;
             }
             return 0;
         });

 

4. Thread serialization method

thenApply method: when a thread depends on another thread, it obtains the result returned by the previous task and returns the return value of the current task.

thenAccept method: consume the processing result. Receive the processing result of the task and consume it. No result is returned.
thenRun method: execute thenRun as long as the above task is completed, but execute the subsequent operations of thenRun after processing the task
With Async, it is executed asynchronously by default. Same as before.
All the above tasks must be completed successfully.
Function<? super T,? extends U>
T: Type U of the result returned by the previous task: the return value type of the current task
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("Current thread:" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("Operation results:" + i);
            return i;
        }, executor).thenApplyAsync(res -> {
            System.out.println("Task 2 started..." + res);
            return "Hello" + res;
        }, executor);
        System.out.println("main......end....." + future.get());

 

 
5. Combination of two tasks - both to be completed

 

Both tasks must be completed to trigger the task.

thenCombine: combine two futures, obtain the return results of the two futures, and return the return value of the current task
Then accept both: combine the two future tasks, obtain the return results of the two future tasks, and then process the tasks without return value.
runAfterBoth: combine two futures. You don't need to obtain the results of the future. You only need two futures to process the task,
Process the task.
 
6. Combination of two tasks - one completed

 

When either of the two future tasks is completed, execute the task.

applyToEither: when one of the two tasks is completed, get its return value, process the task and have a new return value.
acceptEither: one of the two tasks is completed. Get its return value and process the task. There is no new return value.
runAfterEither: one of the two tasks is completed. There is no need to obtain the future results, process the task, and there is no return value.
 
7. Multi task combination

allOf: wait for all tasks to complete

anyOf: as long as one task is completed
 

3, Case

1.MyThreadConfig.java
//@EnableConfigurationProperties(ThreadPoolConfigProperties.class)
@Configuration
public class MyThreadConfig {


    @Bean
    public ThreadPoolExecutor threadPoolExecutor(ThreadPoolConfigProperties pool) {
        return new ThreadPoolExecutor(
                pool.getCoreSize(),
                pool.getMaxSize(),
                pool.getKeepAliveTime(),
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(100000),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );
    }

}

 

2.ThreadPoolConfigProperties.java

@ConfigurationProperties(prefix = "gulimall.thread")
@Component
@Data
public class ThreadPoolConfigProperties {

    private Integer coreSize;

    private Integer maxSize;

    private Integer keepAliveTime;


}

 

3. Use asynchronous threads

@Service("skuInfoService")
public class SkuInfoServiceImpl extends ServiceImpl<SkuInfoDao, SkuInfoEntity> implements SkuInfoService {
    @Resource
    private ThreadPoolExecutor executor;

    
    @Override
    public SkuItemVo item(Long skuId) throws ExecutionException, InterruptedException {

        SkuItemVo skuItemVo = new SkuItemVo();

        CompletableFuture<SkuInfoEntity> infoFuture = CompletableFuture.supplyAsync(() -> {
            //1,sku Acquisition of basic information  pms_sku_info
            SkuInfoEntity info = this.getById(skuId);
            skuItemVo.setInfo(info);
            return info;
        }, executor);


        CompletableFuture<Void> saleAttrFuture = infoFuture.thenAcceptAsync((res) -> {
            //3,obtain spu Sales attribute combination
            List<SkuItemSaleAttrVo> saleAttrVos = skuSaleAttrValueService.getSaleAttrBySpuId(res.getSpuId());
            skuItemVo.setSaleAttr(saleAttrVos);
        }, executor);


        CompletableFuture<Void> descFuture = infoFuture.thenAcceptAsync((res) -> {
            //4,obtain spu Introduction of    pms_spu_info_desc
            SpuInfoDescEntity spuInfoDescEntity = spuInfoDescService.getById(res.getSpuId());
            skuItemVo.setDesc(spuInfoDescEntity);
        }, executor);


        CompletableFuture<Void> baseAttrFuture = infoFuture.thenAcceptAsync((res) -> {
            //5,obtain spu Specification parameter information
            List<SpuItemAttrGroupVo> attrGroupVos = attrGroupService.getAttrGroupWithAttrsBySpuId(res.getSpuId(), res.getCatalogId());
            skuItemVo.setGroupAttrs(attrGroupVos);
        }, executor);


        // Long spuId = info.getSpuId();
        // Long catalogId = info.getCatalogId();

        //2,sku Picture information for    pms_sku_images
        CompletableFuture<Void> imageFuture = CompletableFuture.runAsync(() -> {
            List<SkuImagesEntity> imagesEntities = skuImagesService.getImagesBySkuId(skuId);
            skuItemVo.setImages(imagesEntities);
        }, executor);

        CompletableFuture<Void> seckillFuture = CompletableFuture.runAsync(() -> {
            //3,Remote call query current sku Whether to participate in the second kill discount
            R skuSeckilInfo = seckillFeignService.getSkuSeckilInfo(skuId);
            if (skuSeckilInfo.getCode() == 0) {
                //query was successful
                SeckillSkuVo seckilInfoData = skuSeckilInfo.getData("data", new TypeReference<SeckillSkuVo>() {
                });
                skuItemVo.setSeckillSkuVo(seckilInfoData);

                if (seckilInfoData != null) {
                    long currentTime = System.currentTimeMillis();
                    if (currentTime > seckilInfoData.getEndTime()) {
                        skuItemVo.setSeckillSkuVo(null);
                    }
                }
            }
        }, executor);


        //Wait until all the tasks are completed
        CompletableFuture.allOf(saleAttrFuture,descFuture,baseAttrFuture,imageFuture,seckillFuture).get();

        return skuItemVo;
    }

}

 

Posted by Galahad on Mon, 01 Nov 2021 09:07:33 -0700