构造方法如下
public ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
}
一共四种构造方法,上面的代码是最后一种
前三构造方法个依次是
1.不传后两个参数 threadFactory 和 handler
2.不传最后一个参数 handler
3.不传倒数第二个参数 threadFactory
1、corePoolSize(核心线程数):
向线程池提交任务时,优先创建线程并执行,也可通过 prestartCoreThread() 或 prestartAllCoreThreads() 方法来提前启动线程池中的基本线程。
2、maximumPoolSize(线程池最大容量):
当核心线程数无空闲线程,并且任务队列已满。会创建新的线程,这个参数限制了
核心线程+非核心线程的总数
3、keepAliveTime:(线程存活保持时间):
非核心线程,如空闲时间达到这个标准,会被回收
4、TimeUnit (存活保持时间的单位)
5、workQueue:(任务队列):
用于传输和保存等待执行任务的阻塞队列。
6、threadFactory(线程工厂):
用于创建新线程。threadFactory创建的线程也是采用new Thread()方式,threadFactory创建的线程名都具有统一的风格:pool-m-thread-n(m为线程池的编号,n为线程池内的线程编号)。
7、handler(线程饱和策略):
当线程池和队列都满了,再加入线程会执行此策略。
我画了一个草图,调用execute()时具体流程:
提交 → 核心线程判断 → 任务队列判断 → 最大线程数判断 → 饱和策略
/*
*作者:赵星海
*时间:2020/8/12 9:41
*用途: 线程池
*/
public void testThreadPool() {
//自定义线程池
ThreadPoolExecutor threadPoolExecutor = new
ThreadPoolExecutor(0,0,0,
TimeUnit.DAYS,null, (RejectedExecutionHandler) null);
//四种线程池
Executors.newCachedThreadPool();//高速缓存
Executors.newFixedThreadPool(5);//固执的 #参数:核心线程数&线程总数
Executors.newSingleThreadExecutor();//单一的
Executors.newScheduledThreadPool(5);//预定时间 #参数:核心线程数
}
(1)CachedThreadPool
可以无限创建线程的线程池,空闲线程可存活时间是 60 s
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());
}
(2)FixedThreadPool
有固定大小的线程池。所有线程均为核心线程,线程理论上一直存活。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
(3)SingleThreadExecutor
固定只创建使用一个为核心线程,线程理论上一直存活。后面的任务无线排队
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));
}
(4)ScheduledThreadPool
用来执行 定时 或者 周期性任务
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
ArrayBlockingQueue:有界队列(数组结构)
LinkedBlockingQueue:默认无界(链表结构)内部由单链表实现,只能从head取元素,从tail添加元素。添加元素和获取元素都有独立的锁,也就是说LinkedBlockingQueue是读写分离的,读写操作可以并行执行。LinkedBlockingQueue采用可重入锁(ReentrantLock)来保证在并发情况下的线程安全。
PriorityBlockingQueue:无界 (基于数组的平衡二叉堆)(优先级屏蔽队列)
SynchronousQueue:没实际存储空间,它的工作只有一条,将任务交给闲下来的旧线程执行,或者创建新线程来执行
DelayedWorkQueue: 无界,用数组实现堆排序,具体原理可以看这个https://www.jianshu.com/p/587901245c95
复用核心: 线程执行完任务后会再次循环会调用getTask获取下一个任务
回收核心:getTask→workQueue.poll() 获取不到任务的时候会销毁线程, 空闲线程的存活时间决定于poll()方法
详细:
任务进入队列的过程 execute() →addWorker() 然后入队成功
addWorker()中核心代码是 workers.add();
Worker是什么呢?
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this); //得到线程工厂类,创建线程
}
一个普通的内部类,有两个核心属性 Thread thread 与 Runnable firstTask
它是负责执行任务的执行者,每一个Worker持有一个任务、一个线程
先是设置了 Worker 的State为 -1,是为了避免当前 worker 在调用 runWorker
方法前被中断(当其它线程调用了线程池的 shutdownNow 时候,如果 worker 状态 >= 0 则会中断该线程)。这里设置了线程的状态为 -1,所以该线程就不会被中断了。我们知道当启动一个线程后,native 层C代码会创建一个线程,然后回调到 Runnable的 run方法,所以当Worker对象中的thread被start后,最后也会回调到 run方法。这方面的知识可以查看:https://blog.csdn.net/wuqiqi1992/article/details/107878581
addWorker()方法中核心代码片段: 深海考虑这边代码有点多,只摘取了部分源码
...
Worker w = null;
try {
w = new Worker(firstTask);
final Thread t = w.thread;
...
if (workerAdded) {
t.start();
workerStarted = true;
}
...
t.start(); 意味着线程启动,线程启动会调用C层的代码,然后C层代码会回调到 Runnable 的 run 方法。
看一下worker的run方法:
public void run() {
runWorker(this);
}
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
//status设置为0,允许中断
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {
w.lock();
// If pool is stopping, ensure thread is interrupted;
// if not, ensure thread is not interrupted. This
// requires a recheck in second case to deal with
// shutdownNow race while clearing interrupt
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
//执行任务之前做的事情
beforeExecute(wt, task);
Throwable thrown = null;
try {
//任务执行,这里会调用run方法,这是真正执行任务的地方
task.run();
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
//执行完成后,做的事情
afterExecute(task, thrown);
}
} finally {
task = null;
//统计Worker完成的Task
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
//Worker 完成后执行 清理工作
processWorkerExit(w, completedAbruptly);
}
}
简单描述其过程:
线程执行完任务后下次循环会调用getTask获取任务 当取不到任务时,他会跳出循环然后等待被回收
getTask方法中有这么一段核心代码:
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
//这里说明已经超时了
timedOut = true;
非核心线程会走poll(),核心线程会走take(); poll方法的两个参数决定了poll的阻塞时间,超时后返回空
ThreadPoolExecutor.AbortPolicy:默认策略,丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最长等待时间任务,也就是最前面的任务,然后重新提交被拒绝的任务
ThreadPoolExecutor.CallerRunsPolicy:由提交任务的线程去处理该任务