ThreadPoolExecutor 线程池理论饱和策略工作队列排队策略
Posted mengen
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ThreadPoolExecutor 线程池理论饱和策略工作队列排队策略相关的知识,希望对你有一定的参考价值。
本文链接:https://blog.csdn.net/wangmx1993328/article/details/80582803
目录
本文导读
线程池简述
Executor结构
使用线程池的好处
线程池工作原理
线程池饱和策略
AbortPolicy
DiscardPolicy
DiscardOldestPolicy
用户自定义拒绝策略(最常用)
线程池工作流程图
工作队列排队策略
SynchronousQueue
LinkedBlockingQueue
ArrayBlockingQueue
本文导读
本文主要描述Java线程池的理论知识
Java中有几种方法新建一个线程?
继承Thread或者实现Runnable
使用更高级的线程池
线程池简述
线程池是JDK1.5开始引入的,也叫Executor框架,或是Java并发框架
线程池相关的API在java.util.concurrent包中,常用到以下几个类和接口:
java.util.concurrent.Executor:一个只包含一个方法的接口,它的抽象含义是:用来执行一个Runnable任务的执行器
java.util.concurrent.ExecutorService:继承了Executor接口的接口,增加了很多对于任务和执行器的生命周期进行管理的方法
java.util.concurrent.ThreadFactory:一个生成新线程的接口。用户可以通过实现这个接口管理对线程池中生成线程的逻辑
java.util.concurrent.Executors:创建并返回其余各个实例的类,提供了很多不同的生成执行器的实用方法,比如基于线程池的执行器的实现。
java.util.concurrent.ThreadPoolExecutor:这个类维护了一个线程池,对于提交到此Executor中的任务,它不是创建新的线程而是使用池内的线程进行执行,对于数量巨大但执行时间很短的任务,可以显著地减少对于任务执行的开销。
Executor结构
executor结构主要包括任务、任务的执行和异步结果的计算。
任务:包括被执行任务需要实现的接口,如Runnable接口或Callable接口
任务的执行:包括任务执行机制的核心接口Executor,以及继承自Executor的ExecutorService接口。Executor框架有两个关键类实现了ExecutorService接口(ThreadPoolExecutor和ScheduledThreadPoolExecutor)
异步计算的结果:包括接口Future和实现Future接口的FutureTask类
使用线程池的好处
降低资源消耗:可以重复利用已创建的线程降低线程创建和销毁造成的消耗。
提高响应速度:当任务到达时,任务可以不需要等到线程创建就能立即执行。
提高线程的可管理性:线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控
线程池工作原理
当一个新的任务提交到线程池之后,线程池处理过程如下:
线程池判断核心线程池里的线程是否已满。未满时,则创建一个新的工作线程来执行任务。如果核心线程池里的线程已满,则执行第二步。
线程池判断工作队列是否已经满。如果工作队列没有满,则将新提交的任务存储在这个工作队列里等待执行。如果工作队列满了,则执行第三步。
线程池判断线程池(核心线程池外的线程池部分)的线程是否都处于工作状态。如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则交给饱和策略来处理这个任务。
线程池饱和策略
常用的饱和策略如下:
它们是ThreadPoolExecutor类中的内部类,可以直接调用
AbortPolicy
Java线程池默认的阻塞策略,即不执行此新任务,而且直接抛出一个运行时异常,切记ThreadPoolExecutor.execute需要try catch,否则程序会直接退出。
DiscardPolicy
直接抛弃,新任务不执行,空方法
DiscardOldestPolicy
从队列里面抛弃head的一个任务,并再次execute 此task。
用户自定义拒绝策略(最常用)
实现RejectedExecutionHandler,并自己定义策略模式
线程池工作流程图
以ThreadPoolExecutor为例展示线程池的工作流程
如果当前运行的线程少于corePoolSize(核心线程数),则创建新线程来执行任务(注意,执行这一步骤需要获取全局锁)。
如果运行的线程等于或多于corePoolSize,则将任务加入BlockingQueue(阻塞队列/任务队列)。
如果无法将任务加入BlockingQueue(队列已满),则在非corePool中创建新的线程来处理任务(注意,执行这一步骤需要获取全局锁)。
如果创建新线程将使当前运行的线程超出maximumPoolSize,任务将被拒绝,并执行线程饱和策略,如:RejectedExecutionHandler.rejectedExecution()方法。
ThreadPoolExecutor采取上述步骤的总体设计思路,是为了在执行execute()方法时,尽可能地避免获取全局锁(那将会是一个严重的可伸缩瓶颈)。在ThreadPoolExecutor完成预热之后(当前运行的线程数大于等于corePoolSize),几乎所有的execute()方法调用都是执行步骤2,而步骤2不需要获取全局锁。
工作队列排队策略
已经说过当线程池中工作线程的总数量超过核心线程数量后,新加的任务就会放入工作队列中进行等待被执行
使用线程池就得创建ThreadPoolExecutor对象,通过ThreadPoolExecutor(线程池)类的构造方法创建时,就得指定工作队列,它是BlockingQueue<Runnable>接口,而实际开发中是指定此接口的具体实现类,常用的如下所示。
SynchronousQueue
直接提交策略----意思是工作队列不保存任何任务被等待执行,而是直接提交给线程进行执行。
工作队列的默认选项是 SynchronousQueue,它将任务直接提交给线程而不保存它们。
如果不存在可用于立即运行任务的线程,则试图把任务加入队列将失败,因此会构造一个新的线程。
此策略可以避免在处理可能具有内部依赖性的请求集时出现锁。直接提交通常要求无界 maximumPoolSizes 以避免拒绝新提交的任务。
Executors的newCacheThreadPool()方法创建线程池,就是使用的此种排队策略
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
LinkedBlockingQueue
无界队列策略----无界指的是工作队列大小没有上限,可以添加无数个任务进行等待。
使用无界队列将导致在所有 corePoolSize 线程都忙时新任务在队列中等待。于是创建的线程就不会超过 corePoolSize。因此,maximumPoolSize 的值也就无效了。所以一般让corePoolSize等于maximumPoolSize
当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列
Executors的newFixedThreadPool(int nThreads)方法创建线程池,就是使用的此种排队策略
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
ArrayBlockingQueue
有界队列策略----意思是工作队列的大小是有限制的
优点是可以防止资源耗尽的情况发生,因为如果工作队列被无休止的添加任务也是很危险的
当工作队列排满后,就会执行线程饱和策略
// 构造线程池
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(3, 4,
3, TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(2),
new ThreadPoolExecutor.DiscardOldestPolicy());
如上核心线程为3个,每个线程的工作队列大小为2(即队列中最多有两个任务在等待执行),线程池最大线程数为4个
所以当工作线程数小于等于3时,直接新建线程执行任务;超过3时,任务会被添加进工作队列进行等待,3*2=6,当工作队列等待的任务数超过6个以后,则又会新建一个线程,此时整个线程池线程总数已经达到了4个,当还有任务进行添加时,此时将采取饱和策略
————————————————
版权声明:本文为CSDN博主「蚩尤后裔」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wangmx1993328/article/details/80582803
以上是关于ThreadPoolExecutor 线程池理论饱和策略工作队列排队策略的主要内容,如果未能解决你的问题,请参考以下文章
多线程编程-Executor与ThreadPoolExecutor的使用
Java线程池ThreadPoolExecutor使用和分析 - 终止线程池原理