最近项目的一个业务场景需要使用异步 , 码代码到断断续续完善代码 . 查询了挺多的资料文档, 现在记录一下.
一 : 业务场景
类似QQ 中的设置页面 , 左侧每个选项对应右侧的具体设置 , 项目中暂且简化为需要插入7条数据到不同数据表, 第一条数据和最后一条数据是为了提醒用户开始和结束 。 其中主方法是插入1 ,立刻给用户返回数据(2-6的数据插入费时间,如果都等到插入成功再去返回给用户体验不好), 这时候我们需要开线程去分别执行2-6的插入工作,同时,7的插入必须是在2-6插入之后才可以运行.(2-6是随机的,可能全插入,可能全部不插) 。 做的结果是 : 第一条数据插入数据表,然后就直接返回用户,执行成功,中间2-6的执行过程异步执行,然后第7条数据在异步结束之后插入
二 : 参数
创建一个是一个实体类来存储2-6的所有字段,添加一个标识字段来区分。存入List中 , LIst<T>
三 : 使用技术点
① : spring boot 集成的异步,使用@Async来开启异步,模块启动类添加@EnableAsync 注解
实质上是开启线程执行异步的类,互不干扰执行。
② : CountDownLatch的使用
上面说到,我需要异步执行2-6,返回结果之后才可以去执行7。 所以此时,我们使用到了CountDownLatch工具类来完成线程之间的堵塞。CountDownLatch是一种java.util.concurrent包下一个同步工具类,它允许一个或多个线程等待直到在其他线程中一组操作执行完成。
1、CountDownLatch latch =new CountDownLatch(count); //构造对象时候 需要传入参数count
2、latch .await() 能够阻塞线程 直到调用count次latch .countDown() 方法才释放线程
3、end.countDown() 可以在多个线程中调用 计算调用次数是所有线程调用次数的总和
具体代码如下 :
③ :异步超时处理
因为异步是在后台执行,该项目的异步执行还需要调用其他模块的接口(参考 :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个线程,调用成功。
④ : 异步的事务
在Async 方法上标注@Transactional是没用的。
在Async 方法调用的Service上标注@Transactional 有效。