文章目录
前言
- 内容搜集于网上多篇优秀文章,结合自己理解,写下该文
介绍
-
线程池是一个管理线程的机制,它主要解决两个问题
- 重用线程
频繁的创建和销毁线程会给系统带来额外的开销,所以线程应当得以重用 - 控制线程数量
当线程数量过多时,会出现资源消耗增大,CPU出现过度切换导致并发性能降低,对此线程
的数量也要得以控制在当前硬件环境所能承受的范围内
- 重用线程
-
模式
- 懒汉模式
有任务时临时创建,超过设置线程数时等待,没有任务不创建,没有资源开销,但是响应不好 - 饿汉模式
先创建好设置的线程数,等待任务,任务多于设置的线程数时,也等待
- 懒汉模式
线程池工作流程
- 首先检测线程池运行状态,如果不是RUNNING,则直接拒绝;线程池要保证在RUNNING的状态下才会执行任务
- 如果任务数量<核心线程数,则创建并启动一个线程来执行新提交的任务
- 如果任务数量 >= 核心线程数 && 线程池内的阻塞队列未满,则将该任务添加到该阻塞队列
- 如果任务数量 >=核心线程数 && 任务数量 < 线程最大数量 && 线程池内的阻塞队列已满,则创建并启动一个非核心线程来执行任务
- 如果任务数量>=线程最大数量,并且线程池内的阻塞队列已满, 则使用拒绝策略处理该任务
通过Executors工厂方法创建
- 这种方式底层也是通过自定义方式创建,只是给我们设定好了构造参数
1.可缓存线程池
- 如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小
ExecutorService pool=Executors.newCachedThreadPool();
//等效于:new ThreadPoolExecutor(0,Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>(),Executors.defaultThreadFactory(),defaultHandler);
//将线程放入池中执行任务
execute(t);
2.单线程化的线程池
- 它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行,如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它
ExecutorService pool=Executors.newSingleThreadExecutor();
//等效于:new ThreadPoolExecutor(1,1,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>(),Executors.defaultThreadFactory(),defaultHandler);
//将线程放入池中执行任务
execute(t);
3.定长线程池
- 可控制线程最大并发数,每次执行一个任务就创建一个线程,直到线程达到线程池的最大数量,线程池的大小一旦达到最大值就会保持不变,超出的线程会在队列中等待,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程
ExecutorService pool=Executors.newFixedThreadPool(int nThreads);
//最常用,一般设置为静态,否则创建新对象就会产生新的线程池,导致线程数量不可控
//一般设置成cpu数量-2 int cpuNum=Runtime.getRuntime().availableProcessors()
//等效于:new ThreadPoolExecutor(nThreads,nThreads,0L,TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),Executors.defaultThreadFactory(), defaultHandler);
//将线程放入池中执行任务
void execute(Runnable command);
//提交任务会返回Future对象,get方法可以获取返回值
//异常信息存储在返回future对象里,只有get的时候,才会出现异常信息
Future<?> submit(Runnable task)
Future<T> submit(Runnable task, T result)
Future<T> submit(Callable<T> task)
4.线程池支持定时以及周期性执行任务的需求
4)ScheduledExecutorService pool=Executors.newScheduledThreadPool(intcorePoolSize);
//等效于:new ThreadPoolExecutor(corePoolSize,Integer.MAX_VALUE,0,NANOSECONDS,new DelayedWorkQueue(),Executors.defaultThreadFactory(), defaultHandler);
pool.scheduleAtFixedRate(new Runnable() {
public void run() {
//执行逻辑
}
}, 1000, 5000, TimeUnit.MILLISECONDS);
//从现在开始1秒钟之后,每隔5秒钟执行下一次
pool.scheduleWithFixedDelay(new Runnable() {
public void run() {
//执行逻辑
}
},1000,5000,TimeUnit.SECONDS);
//从现在开始1秒钟之后,任务执行完+5秒,执行下一次
通过构造方法自定义创建
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler);
1.构造参数说明
1.corePoolSize
- 线程池中的核心线程数。默认核心线程一直存活在线程池中,即使它们在线程池中处于闲置状态也不会被回收
- 当allowCoreThreadTimeOut=true时,核心线程闲置时才会根据keepAliveTime回收
2.maximumPoolSize
- 线程池最大线程数。池中最大运行的活动线程(核心+非核心)数
3.keepAliveTime
- 默认非核心线程闲置的超时时间,闲置时间超过这个时间就会被回收
- 当allowCoreThreadTimeOut=true时,超时时间会对核心线程闲置时有效
4.unit
- 用于指定keepAliveTime参数的时间单位,TimeUnit枚举
5.workQueue
- 线程池中保存等待执行的任务的阻塞队列,通过线程池中的execute方法提交的Runable对象都会存储在该队列中
- ArrayBlockingQueue
基于数组实现的有界的阻塞队列,该队列按照FIFO(先进先出)原则对队列中的元素进行排序
new ArrayBlockingQueue(int capacity)
- LinkedBlockingQueue
常用,基于链表实现的有界阻塞队列,该队列按照FIFO(先进先出)原则对队列中的元素进行排序
new LinkedBlockingQueue() //默认指定Integer.MAX_VALUE
new LinkedBlockingQueue(int capacity)
- SynchronousQueue
常用,内部没有任何容量的阻塞队列。在它内部没有任何的缓存空间,对于SynchronousQueue中的数据元素只有当我们试着取走的时候才可能存在 - PriorityBlockingQueue
一个支持优先级排序的无界阻塞队列 - DelayQueue
一个使用优先级队列实现的无界阻塞队列 - LinkedTransferQueue
一个由链表结构组成的无界阻塞队列 - LinkedBlockingDeque
一个由链表结构组成的双向阻塞队列 - implemets BlockingQueue
自定义阻塞队列
6.threadFactory
- 线程工厂,线程池创建新线程使用
7.handler
- 拒绝策略。当任务队列已满并且线程池中的活动线程已经达到限定的最大值或者是无法成功执行任务时的策略
- CallerRunsPolicy
只用调用者所在线程来运行任务 - AbortPolicy
直接抛出RejectedExecutionException异常 - DiscardPolicy
丢弃该任务,不进行处理 - DiscardOldestPolicy
丢弃队列里最近的一个任务,并执行当前任务 - implements RejectedExecutionHandler
自定义
2.API
1.超时时间对核心线程是否有效
void allowCoreThreadTimeOut(boolean value);
boolean allowsCoreThreadTimeOut();
2.线程池提前创建并启动所有核心线程
int prestartAllCoreThreads() //创建线程池后,默认池内没有任何线程,当有任务过来的时候才会去创建
3.执行
void execute(Runnable command)
Future<?> submit(Runnable task)
<T> Future<T> submit(Runnable task, T result)
<T> Future<T> submit(Callable<T> task)
4.是否关闭
boolean isShutdown()
5.活动线程数量
int getActiveCount()
6.线程池曾经运行过的最大线程数量
int getLargestPoolSize()
5.关闭线程池
//不再使用线程池时可以关闭线程池
threadPool.shutdown() //执行完当前任务再销毁池里的线程
threadPool.shutdownNow() //强行打断池里的线程,并销毁