1.为什么要使用线程池
- 反复创建线程开销大
- 过多的线程会占用太多内存
2.线程池的好处
- 加快响应速度
- 合理利用CPU和内存
- 统一管理
3.线程池适用场景
- 服务器接收到大量的请求时,使用线程池技术是非常合适的,它可以大大减少线程的创建和销毁次数,提高服务器的工作效率
- 在开发中,如果需要创建5个以上的线程,就可以使用线程池来处理
4.线程池的参数
- corePoolSize
- 核心线程数,线程池在完成初始化后,默认情况下,线程池中并没有任何线程,线程池会等待有任务到来时再创建新线程去执行任务
- maxPoolSize
- 最大线程数,线程池有可能会在核心线程数的基础上,额外增加一些线程,但是这些新增加的线程数有一个上限
- keepAliveTime
- 如果线程池当前的线程数多于corePoolSize,那么如果多余的线程空闲时间超过keepAliveTime,它们就会被终止
- ThreadFactory
- 新的线程是由ThreadFactory创建的,默认使用Executors.defaultThreadFactory(),创建处理的线程都在同一个线程组,拥有统一的NORM_PRIORITY优先级并且都不是守护线程。如果自己指定ThreadFactory,那么就可以改变线程名、线程组、优先级、是否是守护线程等
- 通常我们用默认的ThreadFactory就可以了
- workQueue
- 直接交换:SynchronousQueue
- 无界队列:LinkedBlockingQueue
- 有界队列:ArrayBlockingQueue
5.添加线程规则
- 如果线程数小于corePoolSize,即使其他工作线程处于空闲状态,也会创建一个新线程来运行新任务
- 如果线程数等于(或大于)corePoolSize但少于maxPoolSize,则将任务放入队列
- 如果队列已满,并且线程数小于maxPoolSize,则创建一个新线程来运行任务
- 如果队列已满,并且线程数大于maxPoolSize,则拒绝该线程
6.增减线程的特点
- 如果设置corePoolSize和maxPoolSize相同,就可以创建固定大小的线程数
- 线程池希望保持较少的线程数,并且只有在负载变得很大时才增加它
- 通过设置maxPoolSize为很高的值,可以允许线程池容纳任意数量的并发任务
- 如果使用的是无界队列(例如LinkedBolckingQueue),那么线程数就不会超过corePoolSize
7. 自带线程池介绍
- newFixedThreadPool
- 由于传进去的LinkedBlockingQueue是没有容量上限的,所以当请求数越来越多,并且无法及时处理完毕的时候,会容易造成占用大量的内存,可能会导致OOM
- newSingleThreadPool
- 和FixedThreadPool的原理基本一样,只不过把线程数直接设置成了1,所以这也会导致同样的问题,可能会占用大量的内存
- newCacheThreadPool
- 无界队列,可以自动回收多余的线程
- maxPoolSize被设置为Integer.MAX_VALUE,这可能会创建数量非常多的线程,甚至导致OOM
- newScheduledThreadPool
- 支持定时以及周期性任务执行
newFixedThreadPool | newSingleThreadPool | newCacheThreadPool | newScheduledThreadPool | |
---|---|---|---|---|
corePoolSize | 构造函数传参 | 1 | 0 | 构造方法传参 |
maxPoolSize | 构造函数传参 | 1 | Integer.MAX_VALUE | Integer.MAX_VALUE |
keepAliveTime | 0 | 0 | 60 | 0 |
workQueue | LinkedBlockingQueue | LinkedBlockingQueue | SynchrousQueue | DelayedWorkQueue |
8.线程池里的线程数量设定为多少比较合适
- CPU密集型(加密,计算hash等):最佳线程数为CPU核心数1-2倍左右
- 耗时IO型(读写数据库、文件、网络独写等):最佳线程数一般会大于cpu核心数很多倍
- 线程数=CPU核心数*(1 + 平均等待时间/平均工作时间)
9.停止线程池的方法
- shutdown:等待线程执行完毕后才终止
- isShutdown:判断是否执行了shutDown方法
- isTerminated:判断线程是否终止
- awaitTermination:判断在指定时间内线程是否终止
- shutDownNoe:立即终止线程,执行中的线程会被中断,队列中的线程会以List返回
10.线程池状态
- RUNNING:接受新任务并处理排队任务
- SHUTDOWN:不接受新任务,但处理排队任务
- STOP:不接受新任务,也不处理排队任务,并中断正在进行的任务
- TIDYING:所有任务都已终止,worderCount为零时,线程会转换到TIDYING状态,并将运行terminate()钩子方法
- TERMINATED:terminate()运行完成
11.使用线程池需要注意的地方
- 避免任务堆积
- 避免线程数过度增加
- 排查线程泄露(线程数量是否超出预期)