Java-线程池
概念
- 创建线程要花费昂贵的资源和时间,如果任务来了才创建那么响应时间就会变成,而且一个进程创建的线程数有限
- 线程池就是首先创建一些线程,它们的集合成为线程池,线程池在系统启动时即创建大量空闲线程,程序将一个任务传给线程池,线程池就会启动一条线程执行这个任务,执行结束后,线程并不会死亡,而是再次返回线程池中成为空闲状态
优势
- 降低资源消耗,通过重复利用已创建的线程降低创建和销毁的花费
- 提高响应速度,任务可以不需要等到线程创建就能立即执行
- 提高线程可管理性,使用线程池可以统一分配,调优和监控
创建
new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,milliseconds,runnableTaskQueue,handler);
- corePoolSize: 线程池基本大小,当向线程池提交一个任务时,若线程池已创建的线程数小于corePoolSize,即便此时存在空闲线程,也会通过创建一个新线程来执行该任务,直到已创建的线程数大于或等于corePoolSize时,(除了利用提交新任务来创建和启动线程(按需构造),也可以通过 prestartCoreThread() 或 prestartAllCoreThreads() 方法来提前启动线程池中的基本线程。)
- maximumPoolSize: 线程池所允许的最大线程个数。当队列满了,且已创建的线程数小于maximumPoolSize,则线程池会创建新的线程来执行任务。对于无界队列,可忽略该参数。
- keepAliveTime: 线程存活时间,当线程池中线程数大于核心线程数时,线程的空闲时间如果超过线程存活时间,那么这个线程就会被销毁,直到线程池中的线程数小于等于核心线程数。
- runnableTaskQueue: 任务队列,用于传输和保存等待执行任务的阻塞队列
- ArrayBlockingQueue: 基于数组结构的有界阻塞队列,FIFO先进先出排序
- LinkedBlockingQueue: 基于链表结构的阻塞队列,FIFO排序,吞吐量高于ArrayBlockingQueue,静态工厂方法
- SynchronousQueue: 不存储元素的阻塞队列,每个插入必须等到另一个进程调用移除操作,否则插入一直处于阻塞状态; 吞吐量高于LinkedBlockingQueue
- PriorityBlockingQueue: 具有优先级的无界阻塞队列
- threadFactory: 线程工厂,用于创建新线程,threadFactory创建的线程也是采用new Thread()方式,threadFactory创建的线程名都具有统一的风格:pool-m-thread-n(m为线程池的编号,n为线程池内的线程编号)。
- handler: 线程饱和策略,当任务太多来不及处理时,用于拒绝任务,实现了RejectedExecutionHandler接口
- AbortPolicy: 直接抛出异常,阻止系统正常运行
- CallerRunsPolicy: 直接在调用者线程中,运行当前被丢弃的任务
- DiscardOlderPolicy: 丢弃最老的一个请求,并执行当前任务
- DiscardPolicy: 丢弃无法处理的任务,不予任何处理
流程

- 首先判断基本线程池是否已满,未满则创建一个工作线程执行任务,满了则下一步
- 线程池判断工作队列是否已满,未满则将新提交的任务存储到工作队列中,满了则下一步
- 线程池判断整个线程池是否已满,未满则创建新的工作线程执行任务,满了则交给饱和策略处理
线程池提交
- 使用execute提交,没有返回值
- 使用submit提交,会返回一个future,通过future.get()获取,阻塞直到任务执行完,而get(long timeout,TimeUnit,unit)方法则会阻塞一段时间后立即返回
Future<Object> future = executor.submit(harReturnValuetask);
try{
Object s = future.get();
}catch (InterruptedException e){
}catch (ExecutionException e){
}finally{
executo.shutdown();
}
线程池的关闭
- 调用线程shutdown或shutdownNow方法,遍历线程池中的的工作线程,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断的任务可能永远无法终止
- 区别在于: shutdownNow将线程池的状态设为STOP,然后尝试停止所有的正在执行或暂定任务的线程,并返回等待执行任务的列表; 而shutdown是将线程池的状态设为SHUTDOWN状态,然后中断所有没有正在执行任务的线程
- 只要调用了两个方法中的任意一个,isShutdown方法就会返回true,而所有的任务都已关闭即所有的线程池关闭,isTerminated才会返回true
线程池的配置
- 任务性质
- CPU密集型任务: 尽量使用较小的线程池,核心数+1,若开过多的线程池,会造成CPU过度切换
- IO密集型任务: 使用稍大的线程池,核心数*2,IO密集型CPU使用率不高,因此CPU在等待IO时有其它线程处理其它任务
- 混合型任务: 可以将任务分成IO密集型和CPU密集型任务,然后分别用不同的线程池去处理
- 任务优先级: 高,中,低
- 任务执行时间: 长,中,短
- 任务依赖性: 是否依赖其他系统资源,如数据库连接
Java提供的线程池
- newCachedThreadPool:用来创建一个可以无限扩大的线程池,适用于负载较轻的场景,执行短期异步任务。(可以使得任务快速得到执行,因为任务时间执行短,可以很快结束,也不会造成cpu过度切换)
- newFixedThreadPool:创建一个固定大小的线程池,因为采用无界的阻塞队列,所以实际线程数量永远不会变化,适用于负载较重的场景,对当前线程数量进行限制。(保证线程数可控,不会造成线程过多,导致系统负载更为严重)
- newSingleThreadExecutor:创建一个单线程的线程池,适用于需要保证顺序执行各个任务
- newScheduledThreadPool:适用于执行延时或者周期性任务
线程池的监控
- taskCount: 线程池需要执行的任务数量
- completedTaskCount: 已完成的任务数量,小于等于taskCount
- largestPoolSize: 曾经创建过的最大线程数量,可以判断线程池是否满过
- getPoolSize: 线程池线程数量,若线程池不销毁的话,线程不会自动销毁
- getActiveCount: 获取活动的线程数