什么场景下使用线程池
- 同步改异步
- 提高并发吞吐量
- 多步任务串行改并行
什么场景下不要使用线程池
- 系统资源已经接近瓶颈(内存、CPU、IO)
- 上游已经有多线程(多线程嵌套、在线程池中创建线程池)
常见线程池
- (1) newFixedThreadPool
建立一个线程数量固定的线程池,规定的最大线程数量,超过这个数量之后进来的任务,会放到等待队列中,如果有空闲线程,则在等待队列中获取,遵循先进先出原则。 - (2) newCacheThreadExecutor
在核心线程达到最大值之前,如果继续有任务进来就会创建新的核心线程,并加入核心线程池;达到最大核心线程数后,新任务进来,优先使用空闲线程,如果没有空闲线程,则新建临时线程.
newCacheThreadExecutor使用的是SynchronousQueue作为等待队列,他不保存任何的任务,新的任务加入进来之后,他会创建临时线程来进行使用。 - (3) newScheduledThreadPool
创建一个线程池,该线程池可以计划在给定的延迟,或周期性地执行。
使用的就是DelayedWorkQueue作为等待队列,中间进行了一定的等待,等待时间过后,继续执行任务。
阿里线程开发规约
线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
说明:Executors 返回的线程池对象的弊端如下:
- 1) FixedThreadPool 和 SingleThreadPool:
允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。 - 2) CachedThreadPool:
允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
多线程优雅下线
为了保障应用重启过程中异步操作的执行,避免强制退出JVM可能产生的各种问题,我们可以采用关闭钩子、自定义信号的方式,主动的通知JVM退出,并在JVM关闭前,执行应用程序的一些扫尾工作,进一步保证应用程序可以安全的退出。
线程在终止的过程中,应该先进行操作来清除当前的任务,保持共享数据的一致性,然后再停止。
使用线程池优雅下线需考量
- 及时终止新增进件
- 尽量保证执行中的任务能够执行完成
- 减少内存中积压的任务,避免任务无法及时完成
方案
- 增加关闭钩子(shutdown hooks),监听进程中断信号。
- 在钩子中将线程池关闭,拒绝新增任务,并等待线程结束。
- 背压:接受任务时控制待处理队列大小,限制内存中积压的任务数量,以保证在停机等待时间范围内能够处理完。
- 限流:可以通过Semaphore信号量限流。
使用线程池要将调度和执行分开,执行代码尽量要有自身完备性。
调度方案:
- 推模式:(外部触发):xxljob, crontab,quartz
- 拉模式:(死循环,timer):数据库扫表, MQ消费
减少内存中积压的任务
- 推模式调度时
在调度入口做进程状态检查,堵塞新任务进件。 - 拉模式调度时考量点
在进程得到中断信号时,及时终止新增任务
拉模式死循环线程终止方案:
- 用共享变量(shared variable)的方式来设置标志,通知线程必须终止。这个共享变量的操作必须保证是同步的。在循环起点判断标志状态
- 处理InterruptedException,在catch中终止循环。
线程池关闭
shutdown()
将线程池状态置为SHUTDOWN,并不会立即停止:
- 停止接收外部submit的任务
- 内部正在跑的任务和队列里等待的任务,会执行完
- 等到第二步完成后,才真正停止
shutdownNow()
将线程池状态置为STOP。企图立即停止,事实上不一定:
- 跟shutdown()一样,先停止接收外部提交的任务
- 忽略队列里等待的任务
- 尝试将正在跑的任务interrupt中断
- 返回未执行的任务列表
awaitTermination(long timeOut, TimeUnit unit)
当前线程阻塞,直到
- 等所有已提交的任务(包括正在跑的和队列中等待的)执行完
- 或者等超时时间到
- 或者线程被中断,抛出InterruptedException
创建线程池参数
ThreadPoolExecutor构造参数:
- corePoolSize 要保留在池中的线程数,也就是线程池核心池的大小
- maximumPoolSize 最大线程数
- keepAliveTime 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。
- unit keepAliveTime 参数的时间单位
- workQueue 用来储存等待执行任务的队列。
- threadFactory 线程工厂
- handler 默认的拒绝执行处理程序