(1)降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
(2)提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
(3)提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
ThreadPoolExecutor使用一个int正整数的高3位来表示线程池状态,低29位表示线程数量
状态:
running、shutsown、stop、tidying、terminated
为什么用一个整数保存?
这些信息存储在一个原子变量ctl中,目的是将线程池状态与线程个数合二为一,这样就可以用一次cas原子操作进行赋值
赋值:
corePoolSize核心线程数目(最多保留的线程数)
maximumPoolSize线程池中最大线程数目
keepAliveTime生存时间-针对救急线程
unit时间单位-针对救急线程
workQueue阻塞队列
threadFactory线程工厂-可以为线程创建时起个好名字 handler拒绝策略
核心线程数+救急线程数=最大线程数
核心线程数 最大线程数分别对应1 2 参数
线程池工作过程
c=2 核心线程是2 m=3 最大线程是3 所以救急线程是1
阻塞队列 size=2
过程:
任务1到来核心线程1 创建并执行
任务2到来核心线程2 创建并执行
任务3到来 放进阻塞队列
任务4到来 放到阻塞队列
任务5到来 创建救急线程并且执行
任务6到来 执行拒绝策略
救急线程执行完任务后,生命周期到了就自动销毁
而核心线程执行完之后,会保留在线程池中,一直存在不会销毁
keepAliveTime、unit 这两个参数控制救急线程的生存时间
救急线程:
如果队列选择了有界队列,那么任务超过了队列大小时,会创建maximumPoolSize - corePoolSize数目的救急线程
拒绝策略:
JDK提供的四种拒绝策略
Executors已经为我们封装好了 4 种常见的功能线程池
newFixedThreadPool
**核心线程数==最大线程数(**没有救急线程被创建),因此也无需超时时间
阻塞队列是无界的,可以放任意数量的任务
评价:使用于任务量已知,相对耗时的任务
public class TestThreadPoolExecutors {
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(2);
pool.execute(()->{
System.out.println(Thread.currentThread().getName()+":1");
});
pool.execute(()->{
System.out.println(Thread.currentThread().getName()+":2");
});
pool.execute(()->{
System.out.println(Thread.currentThread().getName()+":3");
});
}
newCachedThreadPool
核心线程数是0, 最大线程数是Integer.MAX_ _VALUE, 救急线程的空闲生存时间是60s, 意味着全部都是救急线程(60s后可以回收)
救急线程可以无限创建
队列采用了SynchronousQueue 实现特点是,它没有容量,没有线程来取是放不进去的(- 手交钱、一手交货)
特点:
整个线程池表现为线程数会根据任务量不断增长,没有上限,当任务执行完毕,空闲1分钟后释放线程
评价:
适合任务数比较密集,但每个任务执行时间较短的情况
newSingleThread Executor
使用场景
希望多个任务排队执行。线程数固定为1,任务数多于1时,会放入无界队列排队。任务执行完毕,这唯一的线程也不会被释放
区别:
自己创建一个单线程串行执行任务,如果任务执行失败而终止那么没有任何补救措施,而线程池还会新建-个线程,保证池的正常工作
Executors.newSingleThreadExecutor()线程个数始终为1,不能修改
FinalizableDelegatedExecutorService 应用的是装饰器模式,只对外暴露了ExecutorService 接口,因此不能调用ThreadPoolExecutor中特有的方法
Executors.newFixedThreadPool(1)初始时为1,以后还可以修改,对外暴露的是ThreadPoolExecutor对象,可以强转后调用setCorePoolSize等方法进行修改
//执行任务
void execute(Runnable command);
//可以获得任务执行结果
Future submit(Callable task);
例:
shutdown
线程池状态变为SHUTDOWN
不会接收新任务
但已提交的(队列中)任务会执行完
不会中断正在执行的任务
此方法不会阻塞调用线程的执行
shutdownNow
线程池状态变为STOP
不会接收新任务
会将队列中的任务作为方法的返回结果.
中断正在执行的任务
并用interrupt 的方式中断正在执行的任务
其实 Executors 的 4 个功能线程有如下弊端:
FixedThreadPool 和 SingleThreadExecutor:主要问题是堆积的请求处理队列均采用 LinkedBlockingQueue,可能会耗费非常大的内存,甚至 OOM。
CachedThreadPool 和 ScheduledThreadPool:主要问题是线程数最大数是 Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至 OOM。