零、线程池的好处
- 重用存在的线程,减少对象的创建,消亡的开销,性能好
- 可以有效控制最大并发线程数,提高系统资源利用率,同时可以避免过多资源竞争,避免阻塞
- 提供定时执行,定期执行,单线程,并发数控制等功能
一、基本参数介绍
- corePoolSize:核心线程数量
- maximumPoolSize:线程池最大线程数
- workQueue:阻塞队列,存储等待执行的任务
如果运行线程数,小于核心线程数量,则新创建线程来处理请求,即使线程池中尚有空闲线程;
如果运行线程数,大于等于核心线程数,小于线程最大线程数,当阻塞队列满了的时候,创建新线程;
如果corePoolSize和maximumPoolSize大小相等,代表线程池大小固定,新任务提交如果阻塞队列未满,将请求放入阻塞队列;
如果运行线程数,大于最大线程数,且阻塞队列已满的时候,则通过拒绝策略来判断是否将当该任务抛弃或者结束当前线程去执行该任务。
降低资源消耗(上下文切换,cpu使用率,操作系统资源消耗),较大的队列容量,较小的线程池容量,会降低线程池吞吐量;
降低线程阻塞率,增大线程池的容量,cpu使用率会高一些,并发量会增加,cpu调度存在一定的可能性降低性能。
- keepAliveTime:线程没有任务执行时最多保持多久时间终止
- unit:上述时间单位
- threadFactory:线程工厂。使用默认工厂,优先级一致
- rejectHandler:拒绝策略。1>直接抛出异常;2>丢弃队列中最靠前的任务并执行当前任务;3>调用线程来执行当前任务;4>直接丢弃
二、基本方法介绍
============线程池任务执行
execute():提交任务,交给线程池执行
submit(): 提交任务,交给线程池执行,并返回结构 ==== execute+future
============线程池状态相关
shutdown():关闭线程池,等待阻塞队列中任务都做完
shutdownNow():关闭线程池,取消正在执行的线程,不等待任务执行完
=============线程池监控数据
getTaskCount():线程池已执行和未执行任务总数
getCompletedTaskCount():已完成任务数量
getPoolSize():线程池当前线程数量
getActiveCount():当前线程池中正在执行的任务线程数量
三、线程池的状态
图片拷贝自:
- running:接受新提交任务,处理阻塞队列中的任务;新建后即为此状态
- shutdown:关闭状态,不能接受新提交任务,可以处理队列中的任务;running线程池调用 shutdown()方法会进入此状态
- stop:不能接受新的任务,不处理阻塞队列中的任务,会中断正在执行的线程;running线程池调用shutdownNow()方法进入此状态
- tidying:如果所有的任务都已经终止,此时有效线程数为0;
- terminated:仅在tidying状态调用terminated()方法后,得到此状态。
四、java常用线程池
- Executors.newSingleThreadExecutor()
单个线程线程池,保证任务按照指定顺序执行如先进先出,优先级等
- Executors.newSingleThreadScheduledExecutor
定长线程池,定时,周期性任务执行
- Executors.newFixedThreadPool
定长线程池,控制最大并发数,超出的会在阻塞队列中等待
- Executors.newCachedThreadPool
可缓存线程池,超过处理需要可灵活回收空闲线程,没有可以回收使用的则新建线程
五、线程池的合理配置
cpu密集型任务,则可尽量的使用cpu,可奖线程池的大小设置为cpu数量+1;
io密集型任务,参考值可为2*cpu核心数?(参考一般服务,实际上io操作线程数可设置为cpu核心数百倍到千倍)
主要看线程的执行和线程调度时间的比,比值越小,应该不适用于多线程处理。