最近在看java线程池的实现,主要是ThreadPoolExecutor,因此写个博客记录下来。
假设我们创建了一个线程池,其中核心线程数为3,最大线程数为6,任务队列的长度为5。
ThreadPoolExecutor pool = new ThreadPoolExecutor(3, 6, 1, TimeUnit.HOURS, new ArrayBlockingQueue<>(5));
那么根据ThreadPoolExecutor
的doc描述来看,我们可以得到以下的信息
execute(Runnable)
方法时 pool.setCorePoolSize(10);
pool.setMaximumPoolSize(10);
fixed-size thread pool
在线程池构造好之后,线程并不会被马上创建好,只有当有任务提交到线程池中,新线程才会被创建然后开始运行,不过我们可以调用prestartCoreThread
或prestartAllCoreThreads
方法,提前创建出新线程。
线程的创建会由线程工厂来负责,线程工厂可以是由我们指定,也可以使用默认的线程工厂,以默认的线程工厂为例,其创建的所有线程均在同一个线程组中,并且拥有相同的优先级(NORM_PRIORITY
),同时均是非守护线程(这也导致线程池不会自己停掉)。
keepAliveTime
,那么会有2个线程被停止pool.setKeepAliveTime(30, TimeUnit.MINUTES);
pool.allowCoreThreadTimeOut(true);
任务入队的方式和当前线程池的大小有关系,下面具体情况说明
重写以下的方法,在执行任务前和执行任务后,做一些定制的操作。
beforeExecute(Thread, Runnable)
afterExecute(Runnable, Throwable)
public class PausableThreadPoolExecutor extends ThreadPoolExecutor {
private boolean isPaused;
private ReentrantLock pauseLock = new ReentrantLock();
private Condition unpaused = pauseLock.newCondition();
public PausableThreadPoolExecutor(···) {
super(···);
}
@Override
protected void beforeExecute(Thread t, Runnable r) {
super.beforeExecute(t, r);
pauseLock.lock();
try {
while (isPaused)
unpaused.await();
}catch (InterruptedException ie){
t.interrupt();
}finally {
pauseLock.unlock();
}
}
public void pause(){
pauseLock.unlock();
try {
isPaused = true;
}finally {
pauseLock.unlock();
}
}
public void resume(){
pauseLock.lock();
try {
isPaused = false;
unpaused.signalAll();
}finally {
pauseLock.unlock();
}
}
}
private static final int RUNNING = -1 << COUNT_BITS;
此状态下,可以接受新的任务并且执行在队列里的任务
private static final int SHUTDOWN = 0 << COUNT_BITS;
此状态下,可以不接受新的任务,但是可以执行在队列里的任务
private static final int STOP = 1 << COUNT_BITS;
此状态下,不接受新的任务,也不处理在队列里的任务,并且会中断在执行过程中的任务
private static final int TIDYING = 2 << COUNT_BITS;
此状态下,所有的任务都已经终结,工作线程数为0,并且会调用terminated()
勾子方法
private static final int TERMINATED = 3 << COUNT_BITS;
此状态下,terminated()
方法已经完成
类Worker主要是为了维护运行任务的线程的中断控制状态,以及一些其他的次要的记录。
final Thread thread; 记录这个worker运行的线程
Runnable firstTask; 初始化运行的任务
volatile long completedTasks; 完成的任务数
Worker的构造方法需要传递一个Runnable firstTask
来初始化
当worker执行run方法时,会将程序的逻辑委托给外部的runWorker
方法,那么runWorker
的运行流程是什么样的呢?
task
,如果task != null
就执行循环里的逻辑task
的方法——getTask()
setMaximumPoolSize
方法时,就极有可能出现此种情况;(2)当前池处于stopped状态;(3)当前池处于shut down
状态,并且任务队列为空;(4)当前worker获取任务超时,这是由于我们设置了allowCoreThreadTimeOut
或者当前worker数大于核心线程数。beforeExecute(wt, task);
、task.run();
、afterExecute(task, thrown);
分为三部曲
task的执行由调用者执行,也就是由执行execute(Runnable command)
的线程执行,前提是线程池没有shut down
,否则,该任务会被丢弃。
直接抛出异常
直接丢弃任务,不做出任何的提示
丢弃任务队列中的第一个任务(也就是加入队列时间最久的任务),然后重新执行execute(command)
方法