ThreadPoolExecutor线程池的解析
在前一篇文章中我们介绍了
Executors
以及这个线程可以创建的一系列不同类型和作用的线程池,用于各种场景的管理线程,但是在查看这些创建线程的代码中,可以发现,不管是什么哪种线程池,其实本质上都是new了一个ThreadPoolExecutor类,只是传入的参数不同,所以发挥的作用也不同
框架
首先来看看ThreadPoolExecutor相关的类图,
先从顶部看起,可以看到,Executor这个接口只有一个execute的方法
public interface Executor {
void execute(Runnable command);
}
而ExecutorService则是继承了Executor,相比Executor,ExecutorService提供了更多的操作,控制线程的方法。
然后AbstractExecutorService实现了这些方法,然后ThreadPoolExecutor继承了AbstractExecutorService,在ThreadPoolExecutor中在AbstractExecutorService的基础上,对原有的父类的方法进行了重写和拓展,增加了灵活性,例如增加了拒绝策略,addWorker等等
ThreadPoolExecutor参数详解
public ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
//...
}
-
corePoolSize
核心线程数,也就是实际上处理任务的线程数,当提交一个任务的时候,如果线程池当前线程数量小于核心线程数,就会创建一个线程,直到当前线程数等于核心线程数,其他再进来的任务创建的线程将会进到阻塞队列中去。核心线程不会被keepalive策略kill掉,即便他没有任务正在执行。 -
maximumPoolSize
线程池允许最大线程数,当线程池中的阻塞队列满了之后,如果当前的线程还小于最大线程数,那么就会创建线程来执行任务。如果使用的阻塞队列是无界队列,最大线程数这个参数就会失效。 -
keepAliveTime
空闲线程允许存活的时间,由于线程的销毁和创建都会造成性能消耗,因此线程池允许线程执行完任务之后不会立即销毁而是存活一段时间,如果在该时间之内,有其他任务进来,就直接给把线程给这个任务执行,不需要重新创建,节省消耗。不过该参数只对非核心线程有用,因为核心线程不受该参数的约束。 -
unit
这个参数很简单,就是keepAliveTime参数的单位,比如说keepAliveTime数字为60,unit就是设置keepAliveTime时间是60秒,60分钟,还是60小时的 -
workQueue
用来保存等待执行的任务的阻塞队列,等待的任务必须实现Runnable接口。- ArrayBlockingQueue:基于数组结构的有界阻塞队列,FIFO。
- LinkedBlockingQueue:基于链表结构的有界阻塞队列,FIFO。
newSingleThreadExecutor
和newFixedThreadPool
用的就是这个阻塞队列,不过这个队列虽然是有界的,但是线程池默认给了他MAX_VALUE的长度,所以也可以说对于线程池来说是无界的,除非内存满了 - SynchronousQueue:不存储元素的阻塞队列,每个插入操作都必须等待一个移出操作,反之亦然newCachedThreadPool这个线程池用的就是这个队列。所以
- PriorityBlockingQueue:具有优先界别的阻塞队列。
-
threadFactory
真正用于创建线程的工厂,该线程ThreadFactory为一个接口,他是通过newThread()方法提供创建线程的功能public interface ThreadFactory { /** * Constructs a new {@code Thread}. Implementations may also initialize * priority, name, daemon status, {@code ThreadGroup}, etc. * * @param r a runnable to be executed by new thread instance * @return constructed thread, or {@code null} if the request to * create a thread is rejected */ Thread newThread(Runnable r); }
然后在Executors实现了这个接口,DefaultThreadFactory,默认返回一个线程
static class DefaultThreadFactory implements ThreadFactory { private static final AtomicInteger poolNumber = new AtomicInteger(1); private final ThreadGroup group; private final AtomicInteger threadNumber = new AtomicInteger(1); private final String namePrefix; DefaultThreadFactory() { SecurityManager s = System.getSecurityManager(); group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); namePrefix = "pool-" + poolNumber.getAndIncrement() + "-thread-"; } //主要是这个方法 public Thread newThread(Runnable r) { Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0); if (t.isDaemon()) t.setDaemon(false); if (t.getPriority() != Thread.NORM_PRIORITY) t.setPriority(Thread.NORM_PRIORITY); return t; } }
-
RejectedExecutionHandler
拒绝策略,当阻塞队列满了,当前线程数也等于最大线程数的时候,说明线程池已经保护了,此时会对后续再进来的任务执行拒绝策略不让任务再进来,表示,我已经满了,你们别再进来了。线程池提供了四种拒绝策略。- AbortPolicy:直接抛出异常,默认策略;
- CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务
- DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
- DiscardPolicy:直接丢弃任务;但是不抛出异常
线程池状态
线程有五种状态:新建,就绪,运行,阻塞,死亡,线程池同样有五种状态:Running, SHUTDOWN, STOP, TIDYING, TERMINATED。
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// runState is stored in the high-order bits
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;
private static final int TERMINATED = 3 << COUNT_BITS;
RUNNING
: 处于RUNNING
状态的线程池可以接收新任务,也能对新添加的任务进行处理SHUTDOWN
:处于SHUTDOWN
状态的线程池不再接收新任务,但是会对已经添加的,已经在线程池的任务进行处理STOP
:处于STOP
状态的线程不再接收新任务,也不对已经添加的,已经在线程池的任务进行处理,并且会把正在处理的线程中断TIDYING
:当所有的任务已终止,线程池会变为TIDYING
状态,但是线程池还没有消亡TERMINATED
:线程池也被彻底终止的状态,可以理解成线程池的死亡状态
线程池的转化图