异步处理方式探索

这里写自定义目录标题

最佳异步处理方式探索

JDK提供了多种异步处理方式,其中归结出来分为两大类

  • java.lang.Runnable 通过 run()方法新建一个线程处理任务,无返回值
  • java.util.concurrent.Callable 通过call()方法新建线程处理任务,可以获取返回值

适用两种场景:

  1. 不需要获取子线程返回值
  2. 需要获取子线程返回值

以下是公共依赖

package priv.utirx.explore8.controller;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.StopWatch;
import com.google.common.collect.Lists;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import priv.utirx.explore8.feature.api.R;
import priv.utirx.explore8.utils.AsyncUtil;

import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.*;

重要提示: 在获取子线程的执行结果时,务必不要马上获取子线程执行结果。详情请看下面处理方式

CompletableFuture

   @Resource
    private ThreadPoolTaskExecutor taskExecutor;

    @SneakyThrows
    @GetMapping("/completableFuture")
    public R<String> completableFuture() {
        log.info("主线程id: {}, 主线程名称: {}", Thread.currentThread().getId(), Thread.currentThread().getName());
        StopWatch stopWatch = DateUtil.createStopWatch();
        stopWatch.start();

        List<String> list = Lists.newArrayListWithExpectedSize(10);
        List<CompletableFuture<String>> futureList = Lists.newArrayListWithExpectedSize(10);
        for (int i = 0; i < 10; i++) {
            // 异步开启, 子线程执行任务, 因为get()方法是阻塞的,所以主线程一直在等待子线程的返回, 如果此时获取结果,总耗时在20158 毫秒
            // String message = CompletableFuture.supplyAsync(() -> testException(false))
            //         .exceptionally(Throwable::getMessage).get();
            // list.add(message);

            CompletableFuture<String> exceptionally = CompletableFuture.supplyAsync(() -> testException(false))
                    .exceptionally(Throwable::getMessage);
            futureList.add(exceptionally);
        }

        for (CompletableFuture<String> future : futureList) {
            String str = future.get();
            list.add(str);
        }

        list.forEach(System.out::println);
        stopWatch.stop();
        log.info("执行完成,总用时: {} 毫秒", stopWatch.getTotalTimeMillis());
        return R.ok("完成");
    }

    @SneakyThrows
    private String testException(boolean is) {
        log.info("子线程id: {}, 子线程名称: {}", Thread.currentThread().getId(), Thread.currentThread().getName());
        TimeUnit.SECONDS.sleep(1);
        if (is) {
            throw new RuntimeException("账户金额异常");
        }
        TimeUnit.SECONDS.sleep(1);
        return "处理成功";
    }

运行结果:

2021-06-01 17:58:08.934  INFO 20912 --- [nio-8989-exec-2] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2021-06-01 17:58:08.935  INFO 20912 --- [nio-8989-exec-2] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2021-06-01 17:58:08.935  INFO 20912 --- [nio-8989-exec-2] o.s.web.servlet.DispatcherServlet        : Completed initialization in 0 ms
2021-06-01 17:58:08.957  INFO 20912 --- [nio-8989-exec-2] p.u.explore8.controller.AsyncController  : 主线程id: 39, 主线程名称: http-nio-8989-exec-2
2021-06-01 17:58:08.966  INFO 20912 --- [onPool-worker-9] p.u.explore8.controller.AsyncController  : 子线程id: 56, 子线程名称: ForkJoinPool.commonPool-worker-9
2021-06-01 17:58:08.966  INFO 20912 --- [onPool-worker-2] p.u.explore8.controller.AsyncController  : 子线程id: 57, 子线程名称: ForkJoinPool.commonPool-worker-2
2021-06-01 17:58:08.966  INFO 20912 --- [nPool-worker-11] p.u.explore8.controller.AsyncController  : 子线程id: 58, 子线程名称: ForkJoinPool.commonPool-worker-11
2021-06-01 17:58:08.966  INFO 20912 --- [onPool-worker-4] p.u.explore8.controller.AsyncController  : 子线程id: 59, 子线程名称: ForkJoinPool.commonPool-worker-4
2021-06-01 17:58:08.967  INFO 20912 --- [onPool-worker-6] p.u.explore8.controller.AsyncController  : 子线程id: 61, 子线程名称: ForkJoinPool.commonPool-worker-6
2021-06-01 17:58:08.967  INFO 20912 --- [nPool-worker-13] p.u.explore8.controller.AsyncController  : 子线程id: 60, 子线程名称: ForkJoinPool.commonPool-worker-13
2021-06-01 17:58:08.967  INFO 20912 --- [onPool-worker-8] p.u.explore8.controller.AsyncController  : 子线程id: 63, 子线程名称: ForkJoinPool.commonPool-worker-8
2021-06-01 17:58:08.967  INFO 20912 --- [nPool-worker-15] p.u.explore8.controller.AsyncController  : 子线程id: 62, 子线程名称: ForkJoinPool.commonPool-worker-15
2021-06-01 17:58:08.967  INFO 20912 --- [onPool-worker-1] p.u.explore8.controller.AsyncController  : 子线程id: 64, 子线程名称: ForkJoinPool.commonPool-worker-1
2021-06-01 17:58:08.967  INFO 20912 --- [nPool-worker-10] p.u.explore8.controller.AsyncController  : 子线程id: 65, 子线程名称: ForkJoinPool.commonPool-worker-10
处理成功
处理成功
处理成功
处理成功
处理成功
处理成功
处理成功
处理成功
处理成功
处理成功
2021-06-01 17:58:10.993  INFO 20912 --- [nio-8989-exec-2] p.u.explore8.controller.AsyncController  : 执行完成,总用时: 2032 毫秒

ThreadPoolTaskExecutor

线程池配置请查看另一篇文章: 线程池配置

  	@Resource
    private ThreadPoolTaskExecutor taskExecutor;
    
  	@GetMapping("/getResult")
    public R<String> getAsyncResult() {
        StopWatch stopWatch = DateUtil.createStopWatch();
        stopWatch.start();

        List<String> list = Lists.newArrayListWithExpectedSize(10);
        List<Future<String>> submitList = Lists.newArrayListWithExpectedSize(10);
        for (int i = 0; i < 10; i++) {
            log.info("循环遍历, 线程ID: [{}], 线程名称: [{}], 执行批次: ", Thread.currentThread().getId(), Thread.currentThread()
                    .getName());
            // 关键点,开启一个子线程执行任务,而不是同步获取结果,等待执行完成之后再for循环获取
            Future<String> submit = taskExecutor.submit(() -> asyncUtil.pay());
            submitList.add(submit);
        }

        for (Future<String> future : submitList) {
            try {
                list.add(future.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }

        list.forEach(System.out::println);
        stopWatch.stop();
        log.info("执行完成,总用时: {} 毫秒", stopWatch.getTotalTimeMillis());
        return R.ok();
    }

执行结果:

2021-06-01 17:58:39.814  INFO 20912 --- [nio-8989-exec-1] p.u.explore8.controller.AsyncController  : 循环遍历, 线程ID: [38], 线程名称: [http-nio-8989-exec-1], 执行批次: 
2021-06-01 17:58:39.815  INFO 20912 --- [nio-8989-exec-1] p.u.explore8.controller.AsyncController  : 循环遍历, 线程ID: [38], 线程名称: [http-nio-8989-exec-1], 执行批次: 
2021-06-01 17:58:39.815  INFO 20912 --- [nio-8989-exec-1] p.u.explore8.controller.AsyncController  : 循环遍历, 线程ID: [38], 线程名称: [http-nio-8989-exec-1], 执行批次: 
2021-06-01 17:58:39.815  INFO 20912 --- [  self-worker-0] priv.utirx.explore8.utils.AsyncUtil      : 子线程ID: [67], 子线程名称: [self-worker-0] 进入异步执行方法 pay
2021-06-01 17:58:39.815  INFO 20912 --- [nio-8989-exec-1] p.u.explore8.controller.AsyncController  : 循环遍历, 线程ID: [38], 线程名称: [http-nio-8989-exec-1], 执行批次: 
2021-06-01 17:58:39.815  INFO 20912 --- [  self-worker-1] priv.utirx.explore8.utils.AsyncUtil      : 子线程ID: [68], 子线程名称: [self-worker-1] 进入异步执行方法 pay
2021-06-01 17:58:39.815  INFO 20912 --- [nio-8989-exec-1] p.u.explore8.controller.AsyncController  : 循环遍历, 线程ID: [38], 线程名称: [http-nio-8989-exec-1], 执行批次: 
2021-06-01 17:58:39.815  INFO 20912 --- [  self-worker-3] priv.utirx.explore8.utils.AsyncUtil      : 子线程ID: [70], 子线程名称: [self-worker-3] 进入异步执行方法 pay
2021-06-01 17:58:39.815  INFO 20912 --- [nio-8989-exec-1] p.u.explore8.controller.AsyncController  : 循环遍历, 线程ID: [38], 线程名称: [http-nio-8989-exec-1], 执行批次: 
2021-06-01 17:58:39.815  INFO 20912 --- [  self-worker-2] priv.utirx.explore8.utils.AsyncUtil      : 子线程ID: [69], 子线程名称: [self-worker-2] 进入异步执行方法 pay
2021-06-01 17:58:39.816  INFO 20912 --- [nio-8989-exec-1] p.u.explore8.controller.AsyncController  : 循环遍历, 线程ID: [38], 线程名称: [http-nio-8989-exec-1], 执行批次: 
2021-06-01 17:58:39.816  INFO 20912 --- [  self-worker-4] priv.utirx.explore8.utils.AsyncUtil      : 子线程ID: [71], 子线程名称: [self-worker-4] 进入异步执行方法 pay
2021-06-01 17:58:39.816  INFO 20912 --- [  self-worker-5] priv.utirx.explore8.utils.AsyncUtil      : 子线程ID: [72], 子线程名称: [self-worker-5] 进入异步执行方法 pay
2021-06-01 17:58:39.816  INFO 20912 --- [nio-8989-exec-1] p.u.explore8.controller.AsyncController  : 循环遍历, 线程ID: [38], 线程名称: [http-nio-8989-exec-1], 执行批次: 
2021-06-01 17:58:39.816  INFO 20912 --- [nio-8989-exec-1] p.u.explore8.controller.AsyncController  : 循环遍历, 线程ID: [38], 线程名称: [http-nio-8989-exec-1], 执行批次: 
2021-06-01 17:58:39.816  INFO 20912 --- [  self-worker-6] priv.utirx.explore8.utils.AsyncUtil      : 子线程ID: [73], 子线程名称: [self-worker-6] 进入异步执行方法 pay
2021-06-01 17:58:39.816  INFO 20912 --- [nio-8989-exec-1] p.u.explore8.controller.AsyncController  : 循环遍历, 线程ID: [38], 线程名称: [http-nio-8989-exec-1], 执行批次: 
2021-06-01 17:58:39.816  INFO 20912 --- [  self-worker-7] priv.utirx.explore8.utils.AsyncUtil      : 子线程ID: [74], 子线程名称: [self-worker-7] 进入异步执行方法 pay
2021-06-01 17:58:39.817  INFO 20912 --- [  self-worker-9] priv.utirx.explore8.utils.AsyncUtil      : 子线程ID: [76], 子线程名称: [self-worker-9] 进入异步执行方法 pay
2021-06-01 17:58:39.817  INFO 20912 --- [  self-worker-8] priv.utirx.explore8.utils.AsyncUtil      : 子线程ID: [75], 子线程名称: [self-worker-8] 进入异步执行方法 pay
成功
成功
成功
成功
成功
成功
成功
成功
成功
成功
2021-06-01 17:58:40.828  INFO 20912 --- [nio-8989-exec-1] p.u.explore8.controller.AsyncController  : 执行完成,总用时: 1013 毫秒

JDK8并行流处理

请查看我的另一篇文章: 并发编程

(本文完!引用内容务必附原文连接!)

上一篇:北斗再迎新催化:导航精度从米级跨越至厘米级


下一篇:webrtc学习