使用到Java线程池的时候,会有一些核心概念,比如说CPU密集型的任务,核心线程数最好设置为和CPU核数一样,IO密集型的任务核心线程数为CPU核数的两倍。但当遇到具体业务场景的时候,还是单独考虑。

现在有一个业务场景如下:
定期需要处理一批任务,这些任务的数量有多有少,多的时候可能会有5000+,少的时候也能没有。每一个需要做的事情主要有三件,修改数据库的数据,调用RPC请求,变更任务的状态。

具体设计的时候有这么些考虑:
因为涉及到RPC请求的调用,而RPC请求的调用多为IO型任务,所以最好能够异步执行把服务器资源有效率地运行起来。但如果异步任务过多的话,可能会对数据库造成一定程度上的压力。同时,不考虑事务的控制,任务挂了的话,交给下一次定时任务再来处理。
考虑到上面两点,打算用线程池控制异步线程的流量管道,让他不至于太大直接把数据库压垮了,也要让他不至于太小,那么处理任务的效率就太慢了。

设计线程池各项参数的时候,核心线程数没有什么好说的,因为主要压力在IO上,所以是CPU核数的两倍。最大线程数设置为核心线程数的两倍。阻塞队列先进先出,长度设置为5000,拒绝策略为DiscardPolicy,如果这次任务太多的话,就交付给下一次任务处理,尽量不要积压,影响内存的使用。

除了线程池本身的设置之外,在设计的时候还想到一个问题:因为在往线程池里塞的是全量的需要处理的任务数据,同时由于定时任务的设置,把这个过程定时重复执行。也就是说,如果不处理这些已经在线程池队列里的任务的话,整个阻塞队列的任务最终会变成 T1T2T3T4T5 T3T4T5 T5 T6 T7,T1、T2、T3、T4、T5是由于上一次定时任务没有执行完,导致任务状态没有变更而被查询到,延续下来的任务。这样就导致T6、T7的拖延执行和T3、T4、T5的重复执行。

所以需要保证队列里任务的唯一性,T+1次数据库查询出来的数据 与 T次数据库查询出来的数据重复去除掉之后,再加入线程池阻塞队列。