异步化
同步:一件事做完,再做另一件事。
异步:不用等一件事做完,就可以做另一件事,第一件事做好后发送通知。
调用的服务处理能力有限,或是接口处理时长较长时,可以考虑使用异步化的操作。
业务流程分析
标准异步化的业务流程:
1、当用户进行耗时很长的操作时,点击提交后不需要在界面等待,只需要把任务保存到数据库中记录下来。
2、用户要执行新任务时:
a、任务提交成功:任务队列没有满
i、如果程序有多余的空闲线程,可以立即去做
ii、若所有线程都繁忙,放到等待队列中
b、任务提交失败:所有线程都在忙,且任务队列已满
i、可以直接拒绝任务,不再执行
ii、再程序闲的时候,将保存到数据库中的失败记录对应的任务重新执行
3、程序从任务队列中取出任务,依次执行。每完成一件事修改数据库任务状态。
4、用户可以查询任务的执行状态,或者在任务执行成功时得到通知。
5、如果我们要执行的任务非常复杂,包含很多环节,要在每一个小任务完成时记录任务执行状态。
线程池
为什么需要线程池?
1、线程管理比较复杂,比如什么时候新增线程,什么时候减少空闲线程
2、任务存取比较复杂,比如什么时候接受任务,什么时候拒绝任务
线程池可以帮助管理线程
在Spring中,可以使用ThreadPoolTaskExecutor配合@Async来实现(不建议)。
在Java中,可以使用JUC(java.util.concurrent)并发编程包中的ThreadPoolExecutor来实现非常灵活的自定义线程池。
线程池参数:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize(核心线程数=>正式员工):系统应该能同时工作的线程数。
maximumPoolSize(最大线程数=>最多员工):极限情况下线程池的线程数。
keepAliveTime(空闲线程存活时间):非核心线程空闲时长,超时释放。
unit:时长单位。
workQueue(工作队列):按顺序存放给线程执行的任务,阻塞队列,存在队列最大长度,不能无限大。
threadFactory(线程工厂):控制每个线程的生成,线程属性。
handler(拒绝策略):当任务队列满时,采取什么措施(抛异常、优先级、抛弃任务、自定义策略等等)。资源隔离策略(会员一个队列,非会员一个队列)。
线程池步骤:
先招满员工,再堆满任务队列,都满了就新增临时工,临时工满了就调用拒绝策略。
线程池参数设置:
任务分为计算密集型、IO密集型
计算密集型:corePoolSize吃CPU性能,设置为CPU核数+1(空余线程),让每个线程利用好CPU的核,线程之间不用频繁切换。
IO密集型:吃带宽、内存、硬盘读写资源,corePoolSize可以设置大一些,一般2n左右,建议IO能力为主。