异步处理

Callable使用

@GetMapping("/callable")
public Callable<ResJson> testCallable() throws Exception {
log.info("主线程开始!");
ResJson rj = new ResJson();
Callable<ResJson> result = new Callable<ResJson>() {
@Override
public ResJson call() throws Exception {
log.info("副线程开始1!");

CompletableFuture<Boolean> completableFuture = CompletableFuture.supplyAsync(()->{
log.info("副线程开始2!");
return true;
}).thenApplyAsync(d->{
log.info("副线程开始3!");
return true;
});


log.info("副线程结束1!");


rj.setCode(200);
rj.setMsg("成功");
return rj;
}
};
log.info("主线程结束!");
return result;
}

2024-06-27 16:06:56,514 - INFO [http-nio-8806-exec-5] [com.sxsoft.admin.controller.TestController] - <0><14974950867804992> 主线程开始! pos=TestController.java:153
2024-06-27 16:06:56,515 - INFO [http-nio-8806-exec-5] [com.sxsoft.admin.controller.TestController] - <0><14974950867804992> 主线程结束! pos=TestController.java:177
2024-06-27 16:06:56,523 - INFO [MvcAsync4] [com.sxsoft.admin.controller.TestController] - <0><14974950867804992> 副线程开始1! pos=TestController.java:158
2024-06-27 16:06:56,524 - INFO [MvcAsync4] [com.sxsoft.admin.controller.TestController] - <0><14974950867804992> 副线程结束1! pos=TestController.java:169
2024-06-27 16:06:56,524 - INFO [ForkJoinPool.commonPool-worker-1] [com.sxsoft.admin.controller.TestController] - <0><14974949860713280> 副线程开始2! pos=TestController.java:161
2024-06-27 16:06:56,525 - INFO [ForkJoinPool.commonPool-worker-1] [com.sxsoft.admin.controller.TestController] - <0><14974949860713280> 副线程开始3! pos=TestController.java:164

  返回Callable意味着Spring MVC将调用在不同的线程中执行定义的任务。Spring将使用TaskExecutor来管理线程。在等待完成的长期任务之前,servlet线程将被释放。

  在长时间运行的任务执行完毕之前就已经从servlet返回了。这并不意味着客户端收到了一个响应。与客户端的通信仍然是开放的等待结果,但接收到的请求的线程已被释放,并可以服务于另一个客户的请求。

两个概念:

  请求处理线程:处理线程 属于 web 服务器线程,负责 处理用户请求,采用 线程池 管理。

  异步线程:异步线程 属于 用户自定义的线程,可采用 线程池管理。

前端页面等待3秒出现结果。

  注意:异步模式对前端来说,是无感知的,这是后端的一种技术。所以这个和我们自己开启一个线程处理,立马返回给前端是有非常大的不同的,需要注意~

  由此可以看出,主线程早早就结束了(需要注意,此时还并没有把response返回的,此处一定要注意),真正干事的是子线程(交给TaskExecutor去处理的)

  注意:很大程度上提高了我们请求处理线程的利用率,从而肯定就提高了我们系统的吞吐量。

WebAsyncTask升级版callable,增加超时异常等处理

@RequestMapping(value="/longtimetaskTimeout", method = RequestMethod.GET)
    public WebAsyncTask longtimetaskTimeout(){
        System.out.println("/longtimetask被调用 thread id is : " + Thread.currentThread().getId());
        Callable<String> callable = new Callable<String>() {
            public String call() throws Exception {
                Thread.sleep(3000); //假设是一些长时间任务
                System.out.println("执行成功 thread id is : " + Thread.currentThread().getId());
                return "ok";
            }
        };
        WebAsyncTask webAsyncTask = new WebAsyncTask(2000,callable);
        webAsyncTask.onTimeout(()->{
            System.out.println("执行超时 thread id is :" + Thread.currentThread().getId());
            return "执行超时";
        });
        return webAsyncTask;
    }

  这就是前面提到的为什么Callable还要外包一层的缘故,给WebAsyncTask设置一个超时回调,即可实现超时处理,在这个例子中,超时设置为3秒

 

CompletableFuture常见用法 

注:

方法名以”Async“结尾的区别:(如thenApply和thenApplyAsync)

thenApply:当前任务的线程继续执行“thenApply”的任务。

thenApplyAsync:把“thenApplyAsync”这个任务继续交给线程池来进行执行。

 

一、获得结果和触发计算

1、获取结果

get():同Future的get()。

get(long timeout, TimeUnit unit):同Future的get()。

join():同get()。join不报异常,get会报异常。

getNow(T valueIfAbsent):立即获取计算值,如果未计算完,则返回设定的默认值valueIfAbsent。

2、主动触发计算

complete(T value):立即打断异步执行。如果打断成功返回true,此时get会得到设定的默认值value。打断失败返回false,此时get会得到异步执行的结果。

二、对计算结果进行处理

            ...thenApply(i -> {
                return i + 2;
            }).handle((i, e) -> {
                return i + 3;
            })...

i:上一步的计算结果

e:异常

1、thenApply

计算结果存在依赖关系,step by step。

由于存在依赖关系,当前步骤异常时,不会继续下一步。

2、handle

有异常也可以继续下一步,根据带的异常参数可以进一步处理。

 

三、对计算结果进行消费

thenAccept:接收任务的返回结果,并消费处理,无返回结果。

 

四、对计算速度进行选用

applyToEither:返回最快执行完的结果。最快返回后,其他线程依然会继续执行。

 public static void main(String[] args) {
        try {
            CompletableFuture future = CompletableFuture.supplyAsync(() -> {
                try {
                    System.out.println(Thread.currentThread().getName() + "暂停6秒钟");
                    TimeUnit.SECONDS.sleep(6);
                    System.out.println(Thread.currentThread().getName() + "完成6秒钟");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return 6;
            }).applyToEither(CompletableFuture.supplyAsync(() -> {
                try {
                    System.out.println(Thread.currentThread().getName() + "暂停5秒钟");
                    TimeUnit.SECONDS.sleep(5);
                    System.out.println(Thread.currentThread().getName() + "完成5秒钟");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return 5;
            }), r -> r).applyToEither(CompletableFuture.supplyAsync(() -> {
                try {
                    System.out.println(Thread.currentThread().getName() + "暂停4秒钟");
                    TimeUnit.SECONDS.sleep(4);
                    System.out.println(Thread.currentThread().getName() + "完成4秒钟");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return 4;
            }), r -> r);

            System.out.println(future.join());

            try {
                TimeUnit.SECONDS.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } catch (Exception e) {
        }
    }
 
 

复制代码
    public static void main(String[] args) {
        try {
            CompletableFuture future = CompletableFuture.supplyAsync(() -> {
                try {
                    System.out.println(Thread.currentThread().getName() + "暂停6秒钟");
                    TimeUnit.SECONDS.sleep(6);
                    System.out.println(Thread.currentThread().getName() + "完成6秒钟");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return 6;
            }).applyToEither(CompletableFuture.supplyAsync(() -> {
                try {
                    System.out.println(Thread.currentThread().getName() + "暂停5秒钟");
                    TimeUnit.SECONDS.sleep(5);
                    System.out.println(Thread.currentThread().getName() + "完成5秒钟");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return 5;
            }), r -> r).applyToEither(CompletableFuture.supplyAsync(() -> {
                try {
                    System.out.println(Thread.currentThread().getName() + "暂停4秒钟");
                    TimeUnit.SECONDS.sleep(4);
                    System.out.println(Thread.currentThread().getName() + "完成4秒钟");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return 4;
            }), r -> r);

            System.out.println(future.join());

            try {
                TimeUnit.SECONDS.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } catch (Exception e) {
        }
    }
复制代码

 

五、对计算结果进行合并

thenCombine:合并多个任务的执行结果。(如调用多个接口合并处理后返回前端)

public static void main(String[] args) {
        try {
            CompletableFuture future = CompletableFuture.supplyAsync(() -> {
                return 10;
            }).thenCombine(CompletableFuture.supplyAsync(() -> {
                return 20;
            }), (r1, r2) -> {
                return r1 + r2;
            });

            System.out.println(future.join());

            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } catch (Exception e) {
        }
    }

复制代码
    public static void main(String[] args) {
        try {
            CompletableFuture future = CompletableFuture.supplyAsync(() -> {
                return 10;
            }).thenCombine(CompletableFuture.supplyAsync(() -> {
                return 20;
            }), (r1, r2) -> {
                return r1 + r2;
            });

            System.out.println(future.join());

            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } catch (Exception e) {
        }
    }
复制代码

 

posted on 2024-06-27 14:31  五官一体即忢  阅读(2)  评论(0编辑  收藏  举报

导航