一、异步请求与异步调用的区别

两者的使用场景不同:
异步调用是用来做一些非主线流程且不需要实时计算和响应的任务,比如同步日志到kafka中做日志分析,或保存历史数据等。
异步请求用来解决并发请求对服务器造成的压力,从而提高对请求的吞吐量.
响应不同:
异步请求是会一直等待response相应的,需要返回结果给客户端的.
异步调用我们往往会马上返回给客户端响应,完成这次整个的请求,至于异步调用的任务后台自己慢慢跑就行,客户端不会关心。

二、代码示例

1.创建一个模仿耗时操作的业务层

@Service
public class AsyncTaskService {
    
    public String uuid() {
        try {
            TimeUnit.MILLISECONDS.sleep(5000);
            String nowDate=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:S").format(new Date());
            System.out.println(nowDate + "--->工作线程(" + Thread.currentThread().getName() + ")");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return UUID.randomUUID().toString();
    }
    @Async
    public String getUuid() {
        try {
            TimeUnit.MILLISECONDS.sleep(5000);
            String nowDate=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:S").format(new Date());
            System.out.println(nowDate + "--->工作线程(" + Thread.currentThread().getName() + ")");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return UUID.randomUUID().toString();
    }
}

2、创建控制层列举异步请求和异步方法使用场景

@RestController
@EnableAsync
public class AsyncController {
    @Autowired
    AsyncTaskService asyncTaskService;


    /**
     * 异步方法
     * @return
     */
    @GetMapping("asyncMethod")
    public String asyncMethod() {

        System.out.println(LocalDateTime.now().toString() + "--->主线程开始");

        String uuid = asyncTaskService.getUuid();

        System.out.println(LocalDateTime.now().toString() + "--->主线程结束");

        return uuid;

    }

    /**
     * 阻塞请求
     * @return
     */
    @GetMapping("blockingRequest")
    public String blockingRequest() {

        System.out.println(LocalDateTime.now().toString() + "--->主线程开始");

        String uuid = asyncTaskService.uuid();

        System.out.println(LocalDateTime.now().toString() + "--->主线程结束");

        return uuid;

    }

    /**
     * 使用Callable
     * @return
     */
    @GetMapping("callable")
    public Callable<String> callable() {

        System.out.println(LocalDateTime.now().toString() + "--->主线程开始");

        Callable<String> callable = () -> {
            String uuid = asyncTaskService.uuid();
            System.out.println(LocalDateTime.now().toString() + "--->子任务线程("+Thread.currentThread().getName()+")");
            return uuid;
        };

        System.out.println(LocalDateTime.now().toString() + "--->主线程结束");

        return callable;

    }


    /**
     * 使用CompletableFuture
     *
     * 使用该方式时,切记自己创建执行器,不要使用内置的 ForkJoinPool线程池,会有性能问题
     *
     * @return
     */
    @GetMapping("completableFuture")
    public CompletableFuture<String> completableFuture() {

        ExecutorService executor = Executors.newCachedThreadPool();

        System.out.println(LocalDateTime.now().toString() + "--->主线程开始");

        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(asyncTaskService::uuid,executor);

        System.out.println(LocalDateTime.now().toString() + "--->主线程结束");

        return completableFuture;

    }


    /**
     * 使用ListenableFuture
     *
     * 使用该方式时,切记自己创建执行器,不要使用内置的 ForkJoinPool线程池,会有性能问题
     *
     * @return
     */
    @GetMapping("listenableFuture")
    public ListenableFuture<String> listenableFuture() {

        System.out.println(LocalDateTime.now().toString() + "--->主线程开始");

        ListenableFutureTask<String> listenableFuture = new ListenableFutureTask<>(()-> asyncTaskService.uuid());

        Executors.newCachedThreadPool().execute(listenableFuture);

        System.out.println(LocalDateTime.now().toString() + "--->主线程结束");

        return listenableFuture;

    }



    /**
     * 使用WebAsyncTask
     * @return
     */
    @GetMapping("asynctask")
    public WebAsyncTask asyncTask() {

        SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor();

        System.out.println(LocalDateTime.now().toString() + "--->主线程开始");

        WebAsyncTask<String> task = new WebAsyncTask(10000L, executor, ()-> asyncTaskService.uuid());

        task.onCompletion(()->{
            System.out.println(LocalDateTime.now().toString() + "--->调用完成");
        });

        task.onTimeout(()->{
            System.out.println("onTimeout");
            return "onTimeout";
        });


        System.out.println(LocalDateTime.now().toString() + "--->主线程结束");

        return task;
    }

    /**
     * 使用DeferredResult
     * @return
     */
    @GetMapping("deferredResult")
    public DeferredResult<String> deferredResult() {

        System.out.println(LocalDateTime.now().toString() + "--->主线程("+Thread.currentThread().getName()+")开始");

        DeferredResult<String> deferredResult = new DeferredResult<>();

        CompletableFuture.supplyAsync(()->{

            String uuid = asyncTaskService.uuid();

            // int abc = 2/0;

            return uuid;


        }, Executors.newFixedThreadPool(5)).whenCompleteAsync((result, throwable)->{
            if (throwable!=null) {
                deferredResult.setErrorResult(throwable.getMessage());
            }else {
                deferredResult.setResult(result);
            }
        });


        // 异步请求超时时调用
        deferredResult.onTimeout(()->{
            System.out.println(LocalDateTime.now().toString() + "--->onTimeout");
        });

        // 异步请求完成后调用
        deferredResult.onCompletion(()->{
            System.out.println(LocalDateTime.now().toString() + "--->onCompletion");
        });

        System.out.println(LocalDateTime.now().toString() + "--->主线程("+Thread.currentThread().getName()+")结束");

        return deferredResult;
    }
}

3、测试结果
①、当调用异步方法asyncMethod时,页面并未收到相应,控制台打印如下,说明方法异步执行但是不会把结果相应到客户端。

2020-04-23T13:15:29.500--->主线程开始
2020-04-23T13:15:29.500--->主线程结束
2020-04-23 13:15:34:505--->工作线程(SimpleAsyncTaskExecutor-3)

②、当调用blockingRequest时,请求被阻塞,主线程等待工作线程执行完,主线程才会结束。会占用服务器处理请求资源,每次耗时操作执行结束或主线程才会结束。

③、当调用callable及以下异步请求时,主线程不等工作线程结束就结束,工作线程相应结果也会返回客户端,客户端等待工作线程结束后才收到相应

2020-04-23T13:27:48.109--->主线程开始
2020-04-23T13:27:48.130--->主线程结束
2020-04-23 13:27:53:173--->工作线程(MvcAsync1)
2020-04-23T13:27:53.173--->子任务线程(MvcAsync1)