为什么要使用线程池
加快响应速度
合理利用CPU和内存
#线程池适合应用的场合
服务器接收到大量请求时,使用线程池技术是非常合适的,它可以大大减少线程的创建和销毁次数,提高服务器的工作效率
实际上,在开发中,如果需要创建5个以上的线程,那么就可以使用线程池管理
#线程池构造函数的参数
corePoolSize int 核心线程数
maxPoolSize int 最大线程数
keepAliveTime long 保持存活时间
workQueue BlokingQueue 任务储存队列
threadFactory ThreadFactory 当线程池需要新的线程的时候,会使用threadFactory来生成新的线程
Handler RejectedExecutionHandler 由于线程池无法接受你所提交的任务的拒绝策略
#线程添加规则
提交任务 >> 核心线程池(创建核心执行任务) >> 队列(加入到队列) >> 线程池(启动非核心线程池) >> 按照拒绝策略处理
#增减线程特点
1.创建corePoolSize和maximumPoolSize相同,就可以创建固定大小的线程池
2.线程池希望保持较少的线程数量,并且只有在负载变得很大时才增加它
3.maximumPoolSize为很高的值,例如Integer.MAX_VALUE,可以允许线程池容纳任意数量的并发任务
4.队列填满时才创建多于corePoolSize的线程,所以如果是无界队列,那么线程数就不会超过corePoolSize
#keepAliveTime
如果线程池当前的线程数多于corePoolSize,那么多于的线程空闲的时候超过keepAliveTime,他们就会被终止
#ThreadFactory 用来创建线程
新的线程是由ThreadFactory创建的,默认使用Executors.defaultThreadFactory(),创建出来的线程都在同一个线程组,拥有同样的NORM_PRIORITY优先级并且不是守护线程。如果自己制定ThreadFactory,那么就可以改变线程名、线程组、优先级、是否是守护线程等。
#WorkQueue工作队列
1.直接交换 SynchronousQueue (无容量)
2.无界队列 LinkedBlockingQueue(无限容量)
3.有界队列 ArrayBlockingQueue(有限容量)
#线程池里线程数量设置多少合适?
CPU密集型(加密、计算HASH):最佳线程数为CPU核心的1-2倍左右
耗时IO型(读写数据库、文件、网络读写等):最佳线程数一般会大于CPU核心数很多倍,以JVM线程监控显示繁忙情况为依据,保证线程空闲可以衔接上,参考Brain Goetz推荐的计算方法:
线程数 = CPU核心数*(1+平均等待时间/平均工作时间)
FixedThreadPool 固定线程池
CachedThreadPool 可缓存且能够回收
ScheduledThreaPool 定期周期执行任务(定时任务或定时器)
SingleThreadExecutor 固定线程池相同,数量为1
WorKStealingPool 子任务;窃取(线程间会合作)
#停止线程池的正确方法
1.shutdown(初始化关闭过程, 会拒绝新任务,等待存量任务执行完毕)
2.isShutdown 是否发送停止信号
3.isTerminated 是否已经完全停止
4.awaitTermination 是否在指定时间执行完毕(主要用来判断)
5.shutdownNow 立刻关闭线程池
#线程池任务拒绝策略
AbortPolicy 直接抛出异常
DiscardPolicy 静默丢弃
DiscardOldestPolicy 队列中存在最久的数据丢弃
CallerRunsPolicy 提交任务到Caller线程执行(可以让主线程降速)
可以自定义ThreadPoolExecutor实现日志 监控等
#实现原理、源码分析
##线程池组成部分
线程池管理器
工作线程
任务队列
任务接口(Task)
#Executor家族
Executor 顶层接口 —> ExecutorService —> AbstractExecutorService —> ThreadPoolExecutor
#线程池状态
RUNNING:接收新任务并处理排队任务
SHUTDOWN:不接收新任务,但处理派对任务
STOP:不接收新任务,也不处理排队任务,并中断正在执行的任务
TIDYING:所有任务都已经终止
TERMINATED:已经停止,正在关闭
#使用线程池的注意点
避免任务堆积
避免线程数过度增加
避免线程泄露