为什么需要线程池
java线程的创建非常昂贵,需要jvm和os配合完成大量工作,必须为线程堆栈分配和初始化大量内存块,其中包含至少1mb的栈内存,需进行系统调用,以便在os中创建和注册本地线程。频繁创建和销毁线程是非常低效,必须使用线程池,线程池主要解决了两个问题
1)提升性能:线程池能独立负责线程的创建、维护和分配;
2)线程管理:每个java线程池会保持一些基本的线程统计信息;
线程池的类与接口
类/接口 | 简介 |
---|---|
Executor | java异步目标的执行者接口,执行任务目标 |
ExecutorService | 继承Executor 执行者服务接口,对外提供接受异步任务 |
AbstractExecutorService | 抽象类,实现ExecutorService接口,提供默认实现 |
ThreadPoolExecutor | 即为线程池类,继承AbstractExecutorService抽象类 |
ScheduledExecutorServcie | 接口,继承ExecutorService,可以完成延迟和周期性任务的调度。 |
ScheduledThreadPoolExecutor | 继承ThreadPoolExecutor 实现ScheduledExecutorService,类似Timer,高并发程序中性能优于Timer |
线程池的创建与使用
Exeutors 静态工厂里类创建线程池
静态方法 | 简介 |
---|---|
newSingleThreadExecutor() | 创建只有一个线程的线程池,可以保证所有任务按顺序执行 |
newFixedThreadPool(int n) | 创建固定大小的线程池 |
newCachrThreadPool() | 创建不限制线程数量的线程池,任何提交的任务都立即执行 |
newScheduledThreadPool() | 创建可定期或者延时执行任务的线程池 |
由于使用Executors创建的线程池使用无界队列可能导致的内存资源耗尽,或者不限制创建线程个数而导致的CPU资源耗尽等问题, 应该杜绝使用这些便捷方法,而是直接使用线程池ThreadPoolExecutor的构造器。
线程池的标准创建方式
通过构造器ThreadPoolExecutor和ScheduledThreadPoolExecutor的构造方法完成,线程池构造器主要参数如下;
项目 | Value |
---|---|
int corePoolSize 核心线程数 | 即使线程空闲也不会回收 |
int maximumPoolSize | 线程数的上限 |
long keepAliveTime TimeUint unit | 线程最大空闲时长 |
BlockingQueue workQueue | 任务排队的队列 |
ThreadFactory threadFactory | 新线程的产生方式 |
RejectedExecutorionHandler handler | 拒绝策略 |
线程池队列
如果线程池的核心线程都在忙,那么所接收到的目标任务缓存在阻塞队列中,BlockingQueue 线程池阻塞队列是超级接口,实现类有
项目 | Value |
---|---|
ArrayBlockQueue | 基于数组实现的有界阻塞队列,先进先出排序。 |
LinkedBlockingQueue | 基于链表实现的有界阻塞队列,先进先出排序任务 |
PriorityBlockingQueue | 具有优先级的无界阻塞队列 |
DelayQueue | 无界延迟队列 底层基于PriporityBlockingQueue实现,队列每个元素都有过期时间,已过去的元素才会出队。 |
SynchronousQueue | 同步队列,不存储元素的阻塞队列,每个插入操作,必须等到另一个线程的调用移除操作,否则插入操作一直处于阻塞状态,newCachrThreadPool 创建的线程池使用此队列 |
调度器的钩子方法
ThreadPoolExecutor线程池调度器为每个任务执行前后提供了够足方法ThreadPoolExecutor类提供了三个钩子方法
- protected void beforeExecute(Thread t, Runable r) { }
执行任务前的钩子方法 - protected void afterExecute(Runable r,Throwable t){ }
执行任务后的钩子方法 - protected void terminated (){ }
线程池终止时的钩子方法
线程池的拒绝策略
任务被拒绝有两种情况,线程池已关闭或者工作队列已满且maximumPoolSize已满,拒绝策略都会调用 RejectedExecutionHandler 实例的rejectedExecution方法,RejectedExecutionHandler 拒绝策略接口 juc提供了四种实现;
策略种类 | 简介 |
---|---|
AbortPolocy 拒绝策略 | 直接抛出异常,默认策略 |
DiscardPolicy | 抛弃策略 直接抛弃任务,不抛异常 |
DiscardOldestPolicy | 抛弃最老任务策略 |
CallerRunsPolicy | 调用者执行策略,在新任务被添加到线程池时,如果添加失败,那么提交任务线程会自己去执行该任务,不会使用线程池中的线程去执行新任务。 |
线程池状态
状态 | 简介 |
---|---|
runing | 线程池创建后的初始状态,可以执行任务 |
shutdown | 不接受新任务 ,工作队列中的任务执行完毕 |
stop | 不接受新任务,不处理队列中的任务,并中断所有工作线程 |
tidying | 所有任务已终止或处理完成,将会执行 terminated()钩子方法 |
terminated | 执行terminated()钩子方法后的状态 |
线程池关闭的三个方法
- shutdown 方法关闭线程池,拒绝接受新任务,添加新任务会抛出异常,线程池不会立刻退出,知道添加到线程池的任务执行完成才退出。
- shutdownnow方法,线程池会立即变成stop,并试图停止正在执行的线程,不再处理阻塞队列中等待的任务。
- awaitTermination 等待线程池完成关闭,才返回。在调用线程池的shutdown()与shutdownNow()方法时,当前线程会立即返回,不会一直等待直到线程池完成关闭。如果需要等到线程池关闭完成,可以调用awaitTermination()方法。