Thread pool and asynchronous orchestration

Keywords: Java

Seven parameters of thread pool

/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters.
*
* @param corePoolSize the number of threads to keep in the pool, even
*        if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
*        pool
* @param keepAliveTime when the number of threads is greater than
*        the core, this is the maximum time that excess idle threads
*        will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
*        executed.  This queue will hold only the {@code Runnable}
*        tasks submitted by the {@code execute} method.
* @param threadFactory the factory to use when the executor
*        creates a new thread
* @param handler the handler to use when execution is blocked
*        because the thread bounds and queue capacities are reached
  1. corePoolSize: the number of threads that remain in the pool, even if the threads are idle. Unless allowCoreThreadTimeOut is set
  2. maximumPoolSize: the maximum number of threads allowed in the pool
  1. keepAliveTime: when the number of threads is greater than the number of core threads, the threads exceeding the number of core threads will terminate the release if they do not receive new tasks for a maximum period of time. Finally, the thread pool will be maintained at the size of corePoolSize
  2. Unit: time unit
  1. workQueue: a blocking queue used to store tasks waiting to be executed. If the current demand for threads exceeds the size of corePoolSize, it will be placed here for idle threads to execute
  2. threadFactory: a factory that creates threads, such as specifying thread names
  1. 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 queue to get the 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 max core number of idle threads will be automatically destroyed after the time specified by keepAliveTime. 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 for processing

3. All threads are created by the specified factory.

interview:

A thread pool core 7; max 20, queue: 50100 how to allocate concurrent entries;

First, 7 can be executed directly, then 50 enter the queue and continue to execute after opening 13 more. Now 70

It was arranged. The remaining 30 use the reject policy.

Create asynchronous operation

Create asynchronous object

Completable future provides four static methods to create an asynchronous operation.

1. runXxxx does not return results, and supplyxx can obtain the returned results

2. You can pass in a custom thread pool, otherwise the default thread pool will be used

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 that executes the current task executes the task that continues to execute whenComplete. whenCompleteAsync: submit whenCompleteAsync to the thread pool for execution. The method does not end with Async, which means that the Action is executed with the same thread, and Async may be executed with other threads (if the same thread pool is used, it may also be selected for execution by the same thread)

handle method

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

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

Combination of two tasks - both to be completed

Both tasks must be completed to trigger the task. thenCombine: combine two futures to obtain the return results of two futures and return the return value of the current task. thenAcceptBoth: combine two futures to obtain the return results of two future tasks, and then process the task without return value. runAfterBoth: combine two futures. You don't need to obtain the results of the future. You only need to process the task after the two futures process the task.

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

Example code:

@Override
    public OrderConfirmVo confirmOrder() throws ExecutionException, InterruptedException {

        //Build OrderConfirmVo
        OrderConfirmVo confirmVo = new OrderConfirmVo();

        //Get the login information of the current user
        MemberResponseVo memberResponseVo = LoginUserInterceptor.loginUser.get();

        //TODO: get the request header information of the current thread (solve the problem of losing the request header in Feign asynchronous call)
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

        //Start the first asynchronous task
        CompletableFuture<Void> addressFuture = CompletableFuture.runAsync(() -> {

            //Each thread shares the previous request data
            RequestContextHolder.setRequestAttributes(requestAttributes);

            //1. Remote query of all harvest address lists
            List<MemberAddressVo> address = memberFeignService.getAddress(memberResponseVo.getId());
            confirmVo.setMemberAddressVos(address);
        }, threadPoolExecutor);

        //Start the second asynchronous task
        CompletableFuture<Void> cartInfoFuture = CompletableFuture.runAsync(() -> {

            //Each thread shares the previous request data
            RequestContextHolder.setRequestAttributes(requestAttributes);

            //2. Remote query of all selected shopping items in shopping cart
            List<OrderItemVo> currentCartItems = cartFeignService.getCurrentCartItems();
            confirmVo.setItems(currentCartItems);
            //feign needs to construct requests and call many interceptors before remote calls
        }, threadPoolExecutor).thenRunAsync(() -> {
            List<OrderItemVo> items = confirmVo.getItems();
            //Get the id of all products
            List<Long> skuIds = items.stream()
                    .map((itemVo -> itemVo.getSkuId()))
                    .collect(Collectors.toList());

            //Remote query of commodity inventory information
            R skuHasStock = wmsFeignService.getSkuHasStock(skuIds);
            List<SkuStockVo> skuStockVos = skuHasStock.getData("data", new TypeReference<List<SkuStockVo>>() {});

            if (skuStockVos != null && skuStockVos.size() > 0) {
                //Convert the skuStockVos set to a map
                Map<Long, Boolean> skuHasStockMap = skuStockVos.stream().collect(Collectors.toMap(SkuStockVo::getSkuId, SkuStockVo::getHasStock));
                confirmVo.setStocks(skuHasStockMap);
            }
        },threadPoolExecutor);

        //3. Query user points
        Integer integration = memberResponseVo.getIntegration();
        confirmVo.setIntegration(integration);

        //4. Automatic calculation of price data

        //TODO 5. Anti duplication token (to prevent repeated submission of forms)
        //Set a token for the user with a 30 minute expiration time (redis exists)
        String token = UUID.randomUUID().toString().replace("-", "");
        redisTemplate.opsForValue().set(USER_ORDER_TOKEN_PREFIX+memberResponseVo.getId(),token,30, TimeUnit.MINUTES);
        confirmVo.setOrderToken(token);


        CompletableFuture.allOf(addressFuture,cartInfoFuture).get();

        return confirmVo;
    }

Posted by melqui on Sun, 21 Nov 2021 02:24:08 -0800