目录
学习资料
《Java并发编程的艺术》第9章
1.简介
线程池好处:
- 降低资源消耗:线程创建销毁会消耗资源
- 提高响应速度
- 便于管理线程
2.线程池实现原理
线程池要素:
corePool
:核心线程池大小maximunPool
:最大线程池大小BlockingQueue<Runnable>
:任务(阻塞)队列,四种ArrayBlockingQueue
:数组实现有界阻塞队列,FIFOLinkedBlockingQueue
:基于链表阻塞队列,FIFO,吞吐量高于ArrayBlockingQueueSynchronousQueue
:不存储元素的阻塞队列,吞吐量高于LinkedBlockingQueuePriorityBlockingQueue
:一个具有优先级的阻塞队列
RejectedExecutionHandler
:拒绝(饱和)策略,四种AbortPolicy
:抛出异常CallerRunsPolicy
:用当前线程运行任务DiscardOldestPolicy
:丢弃队列里最近一个任务,并执行当前任务DiscardPolicy
:不处理,丢掉
线程执行任务时会经历4个阶段:(阻塞队列不同可能具体行为不同)
- 核心:当前运行的线程少于
corePoolSize
,新建线程并执行 - 阻塞:当前的线程数>=
corePoolSize
,将任务加入BlockingQueue
- 最大:无法加入
BlockingQueue
,创建新的线程来处理任务(需要获取全局锁) - 拒绝:如果创建新线程将使当前运行的线程超出
maximumPoolSize
,则拒绝,调用拒绝策略
ThreadPoolException中执行任务示意图:
线程执行任务分两种情况:
- 创建线程时提交的任务
- 反复从BlockingQueue获取任务执行
3.线程池的使用
3.1 创建线程池
七个参数:
corePoolSize
:核心线程池数量,调用prstartAllCoreThreads()
方法会提前启动所有线程maximumPoolSize
:最大线程池数量,无界队列该参数没效果keepAliveTime
:当前的线程数量大于corePoolSize,指定时间后超过的空闲线程销毁unit
:keepAliveTime的单位workQueue
:任务队列,4种threadFactory
:线程工厂handler
:拒绝策略,4种
3.2 提交任务
两个方法:
execute(runnable)
:提交不需要返回值的任务submit(runnable)
:提交需要返回值的任务- 会返回一个future类型的对象
- 可以通过
future.get()
来阻塞当前线程,直到返回工作线程结果 - 可以使用超时方法
get(long timeout,TimeUnit unit)
3.3 关闭线程池
两种方式:shutdown()
或shutdownNow()
,原理都是 遍历+interrupt方法
shutdown()
:不会停止正在执行的线程,常用shutdownNow()
:会尝试停止所有线程,不常用
两个判断方法:
isShutDown()
:是否调用了上面两个方法之一isTerminated()
:是否所有线程停止
3.4 配置线程池
分析任务特性:
- 性质:CPU密集型,IO密集型还是混合型
- 优先级:高,中,低
- 执行时间:长,中,短
- 依赖性:是否依赖其他资源,如数据库
性质不同规模的线程池分开处理:
-
CPU密集型:线程数配置尽可能小,如
CPU个数+1
-
IO密集型:应配置尽可能多的线程,如
2*CPU数
-
混合型任务:拆分为一个CPU密集型和一个IO密集型,如果执行时间相差很大,不要拆
-
获取CPU数量:
Runtime.getRuntime().avaliableProcessors();
依赖数据库连接池的任务,等待时间越长,CPU空闲时间越长,线程数应设置越大
建议使用有界队列,能增加系统稳定性和预警能力
3.5 线程池监控
系统中使用了大量线程池,可以通过线程池提供的参数进行监控,可以get以下属性:
taskCount
:线程池需要执行的任务数量completedTaskCount
:已完成的任务数量largestPoolSize
:线程池里曾经创建过的最大线程数量getPoolSize
:线程池的线程数量getActiveCount
:获取活动的线程数
也可以扩展线程池方法,重写以下方法:
beforeExecute(..)
:执行前afterExecute(..)
:执行后terminated(..)
:关闭前
4.学习总结
1.线程池简介
- 是什么
- 好处:降低资源消耗,提高响应速度,便于管理线程
2.线程池实现原理
线程池组成:
- corePool:核心池
- maximumPool:最大池
- BlockingQueue:阻塞队列 (四种)
- ArrayBlockingQueue, LinkedBlockingQueue
- SynchronousQueue(不存储元素)
- PriorityBlockingQueue(优先级)
- 饱和拒绝策略:(四种)
抛出异常,当前线程执行,丢弃前一个,直接丢弃
线程池执行四个阶段:
核心池—>阻塞—>最大池—>拒绝
3.线程池的使用
- 创建线程池:七个参数
两个大小(核心池,最大池),两个时间(空闲时间,单位),工厂,阻塞队列,拒绝 - 提交任务:
execute()
:无返回值submit()
:返回future
- 关闭线程池
shutdown()
:正常终止,执行完队列中的任务shutdownNow()
:立即终止isShutDown()
:是否调用了上方法isTerminated()
:是否所有线程停止
- 配置线程池
- 分析任务特性:IO密集,CPU密集, 混合型
- 性质不同分配不同数量线程:
- CPU密集型:少,cpu数+1
- IO密集型:多,2倍cpu数
- 获取CPU数:
Runtime.getRuntime().avaliableProcessors();
- 建议使用有界队列
- 线程池监控
- 监控参数:
taskCount
:线程池需要执行的任务数量completedTaskCount
:已完成的任务数量largestPoolSize
:线程池里曾经创建过的最大线程数量getPoolSize
:线程池的线程数量getActiveCount
:获取活动的线程数
- 扩展:
beforeExecute(..)
:执行前afterExecute(..)
:执行后terminated(..)
:关闭前
- 监控参数: