这里写自定义目录标题
最佳异步处理方式探索
JDK提供了多种异步处理方式,其中归结出来分为两大类。
- java.lang.Runnable 通过 run()方法新建一个线程处理任务,无返回值
- java.util.concurrent.Callable 通过call()方法新建线程处理任务,可以获取返回值
适用两种场景:
- 不需要获取子线程返回值
- 需要获取子线程返回值
以下是公共依赖
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并行流处理
请查看我的另一篇文章: 并发编程
(本文完!引用内容务必附原文连接!)