最近项目的一个业务场景需要使用异步 , 码代码到断断续续完善代码 . 查询了挺多的资料文档, 现在记录一下.
一 : 业务场景
类似 QQ 中的设置页面 , 左侧每个选项对应右侧的具体设置 , 项目中暂且简化为需要插入 7 条数据到不同数据表, 第一条数据和最后一条数据是为了提醒用户开始和结束 . 其中主方法是插入 1 , 立刻给用户返回数据 (2-6 的数据插入费时间, 如果都等到插入成功再去返回给用户体验不好), 这时候我们需要开线程去分别执行 2-6 的插入工作, 同时, 7 的插入必须是在 2-6 插入之后才可以运行.(2-6 是随机的, 可能全插入, 可能全部不插) . 做的结果是 : 第一条数据插入数据表, 然后就直接返回用户, 执行成功, 中间 2-6 的执行过程异步执行, 然后第 7 条数据在异步结束之后插入
页面类型举例
二 : 参数
创建一个是一个实体类来存储 2-6 的所有字段, 添加一个标识字段来区分. 存入 List 中 , LIst<T>
三 : 使用技术点
1 : spring boot 集成的异步, 使用 @Async 来开启异步, 模块启动类添加 @EnableAsync 注解
实质上是开启线程执行异步的类, 互不干扰执行.
2 : CountDownLatch https://www.cnblogs.com/cuglkb/p/8572239.html 的使用
上面说到, 我需要异步执行 2-6, 返回结果之后才可以去执行 7. 所以此时, 我们使用到了 CountDownLatch 工具类来完成线程之间的堵塞. CountDownLatch 是一种 java.util.concurrent 包下一个同步工具类, 它允许一个或多个线程等待直到在其他线程中一组操作执行完成.
1,CountDownLatch latch =new CountDownLatch(count); // 构造对象时候 需要传入参数 count
2,latch .await() 能够阻塞线程 直到调用 count 次 latch .countDown() 方法才释放线程
3,end.countDown() 可以在多个线程中调用 计算调用次数是所有线程调用次数的总和
具体代码如下 :
异步调用代码展示
3 : 异步超时处理
因为异步是在后台执行, 该项目的异步执行还需要调用其他模块的接口 (参考 :spring cloud eureka 的使用), 导致执行速度慢, 考虑到异步可能会无限执行下去, 消耗资源, 我们添加异步超时的处理.
这里使用了 Future<T> 接口.
boolean cancel(boolean mayInterruptIfRunning); 取消正在执行的异步, 取消成功返回 true, 取消失败返回 false. 参数填 true 既取消异步, 填 false 既不取消异步.
boolean isCancelled(); 任务是否被取消成功, 如果在任务正常完成前被取消成功, 则返回 true.
boolean isDone(); 表示任务是否已经完成, 完成返回 true , 反之 false.
V get()throws InterruptedException, ExecutionException; 获取异步线程的执行结果, 这个方法是存在堵塞效果的, 任务执行完成之后就会返回执行结果, true 或 false 返回的数据类型预 Future<T> 的 T 一致
Vget(long timeout, TimeUnit unit)throws InterruptedException, ExecutionException, TimeoutException; 设置超时时间, 设置时间内异步完成则则成功, 超时则返回失败, 返回的数据类型预 Future<T> 的 T 一致
参考上图, 我们使用到了取消和设置超时时间的方法
注意 : 在写代码的过程中, 我们一开始是直接在 controller 上面开启异步, 2-6 直接在异步类中开启超时, 这时候不生效. 查看原因是我们在异步类中的总方法调用另外五个异步, 他们实际上还是一个线程. 此时我们在 service 中调用, 主方法的异步在 service 层, 剩下五个小的异步在异步类, 此时是 6 个线程, 调用成功.
4 : 异步的事务
在 Async 方法上标注 @Transactional 是没用的.
在 Async 方法调用的 Service 上标注 @Transactional 有效.
来源: http://www.jianshu.com/p/712c60c43456