Java线程池
线程池
32708534-318e-49b1-9014-ed7e7803c392
核心类 — ThreadPoolExecutor
构造方法
/**
* 用给定的初始参数创建一个新的ThreadPoolExecutor。
*/
public ThreadPoolExecutor(int corePoolSize,//线程池的核心线程数量
int maximumPoolSize,//线程池的最大线程数
long keepAliveTime,//当线程数大于核心线程数时,多余的空闲线程存活的最长时间
TimeUnit unit,//时间单位
BlockingQueue<Runnable> workQueue,//任务队列,用来储存等待执行任务的队列
ThreadFactory threadFactory,//线程工厂,用来创建线程,一般默认即可
RejectedExecutionHandler handler//拒绝策略,当提交的任务过多而不能及时处理时,我们可以定制策略来处理任务
) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
最重要的三个参数:
- corePoolSize:任务队列未达到最大容量时,最多可以同时运行的线程数量。
- maximumPoolSize:任务队列存放到达任务队列容量后,当前可以同时运行的线程数量变为最大线程数。
- workQueue:新任务来的时候会判断当前运行的线程数量是否达到核心线程数,如果达到的话,新任务就会存放在队列中。
其他参数:
- keepAliveTime:线程池中的线程数量大于 corePoolSize 时,如果这时没有新任务需要执行,非核心线程会等待 keepAliveTime 后才回收销毁。
- unit:keepAliveTime 参数的时间单位。
- threadFactory:executor创建新线程的时候会用到。
- handler:拒绝策略。
各个参数间的关系图
拒绝策略
如果当前同时运行的线程数量达到了最大线程数,且任务队列也已经满了,此时会对新提交的任务有一些处理策略。
ThreadPoolExecutor.AbortPolicy
:抛出 RejectedExecutionException 来拒绝新任务的提交ThreadPoolExecutor.CallerRunsPolicy
:调用自己的线程去运行任务,如果执行线程已经关闭,则会丢弃该任务。ThreadPoolExecutor.DiscardPolicy
:不处理新任务,直接丢弃掉。ThreadPoolExecutor.DiscardOldestPolicy
:此策略将丢弃最早的未处理的任务请求。
通过 Executors创建线程池的问题
新任务在来的时候会先判断当前运行的线程数量是否达到核心线程数,如果达到会将任务放在任务队列中。
而在Executors中选用了不同的任务队列,形成了不同的线程池:
LinkedBlockingQueue
: 无界队列,容量为Integer.MAX_VALUE
。FixedThreadPool
和SingleThreadExector
使用该队列。由于队列不会满,当任务过多时可能会OOMSynchronousQueue
:同步队列,无容量。CachedThreadPool
使用该队列。由于没有容量,所以每次有个任务提交都会尝试创建线程去进行处理,所以线程数可以无限扩展,可能会创建过多线程导致OOM。DelayedWorkQueue
:延迟阻塞队列,满容后会自动扩容1/2,最大容量为Integer.MAX_VALUE
。ScheduledThreadPool
和SingleThreadScheduledExecutor
使用该队列。内部是优先队列,按照延迟的时间长短对任务排序,保证每次出队的任务都是当前队列中执行时间最靠前的。 因为容量问题可能会OOM。
线程池原理
-
如果当前运行的线程数小于核心线程数,那么就会新建一个线程来执行任务。
-
如果当前运行的线程数等于或大于核心线程数,但是小于最大线程数,那么就把该任务放入到任务队列里等待执行。
-
如果向任务队列投放任务失败(任务队列已经满了),但是当前运行的线程数是小于最大线程数的,就新建一个线程来执行任务。
-
如果当前运行的线程数已经等同于最大线程数了,新建线程将会使当前运行的线程超出最大线程数,那么当前任务会被拒绝,饱和策略会调用
RejectedExecutionHandler.rejectedExecution()
方法