线程池的基本知识
- 什么是线程池
- 在平时使用线程的过程中,如果要一次性创建或者是使用多个线程,可以通过一个线程容器来管理这些线程,这里的线程容器就是线程池。那么为什么要用线程池?这是因为在平时的使用过程中,这个线程容器可以对线程进行统一分配,调优和监控。并且他可以通过维护线程的数量来避免大量线程创建撤销带来的系统开销,这也就意味着资源消耗的减少。
- 线程池参数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
线程池中比较重要的参数一共有六个,在详细介绍线程池之前首先来看一下参数。
- - corePollSize
线程池中指定的线程数目,当线程池中线程数目大于该值,多于的线程放入队列中。
- - maximumPoolSize
最大线程数目,当队列中已经放满线程时,可以扩大线程池中容纳线程数量到该值,线程数目比该值大时就会执行抛弃策略。
- - keepAliveTime
线程池数目多于线程池指定线程数目的时候,多于的线程在线程池中存在的时间。
- - unit
keepAliveTime的时间单位,有毫秒、秒、分、时等等单位。
- - workQueue
任务队列,这里用于存放多于指定线程数目的线程。这里的任务队列主要有四种。
- ArrayBlockingQueue:有界的阻塞队列,它的底层是一个数组,通过一个可重入锁来保证操作线程安全。
- LinkedBlockingQueue:无界的阻塞队列,基于链表结构实现,它的底层使用两把锁实现线程安全,分别作用于队首和队尾,因此其吞吐量要高于ArrayBlockingQueue。
- SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。
- PriorityBlockingQueue:一个具有优先级的无限阻塞队列。总是确保优先级最高的线程执行。
- - handle
拒绝策略,当线程太多以至于队列中都放不下,并且线程数目超过线程池最大线程容量。就需要执行拒绝策略,总共有四种拒绝策略。
Abort:当线程数量过多,这种策略会直接抛出异常。
Discard:线程池会默默的丢弃线程,不抛出异常。
Discardoldest:抛弃线程池中最老的一个请求,然后尝试提交当前线程。
CallerRuns:让调用线程池的主线程来执行多于的线程。
- 常用的线程池
Executors默认提供了一些常用的封装好的线程池。
- newFixedThread
/**
* Creates a thread pool that reuses a fixed number of threads
* operating off a shared unbounded queue. At any point, at most
* {@code nThreads} threads will be active processing tasks.
* If additional tasks are submitted when all threads are active,
* they will wait in the queue until a thread is available.
* If any thread terminates due to a failure during execution
* prior to shutdown, a new one will take its place if needed to
* execute subsequent tasks. The threads in the pool will exist
* until it is explicitly {@link ExecutorService#shutdown shutdown}.
*
* @param nThreads the number of threads in the pool
* @return the newly created thread pool
* @throws IllegalArgumentException if {@code nThreads <= 0}
*/
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
这个线程池中核心线程数目和最大线程数目相等,没有等待时间并且使用一个无界的阻塞队列来充当任务队列,由于这里没有指定队列的大小,因此默认的队列大小是Integer.MAX_VALUE,这基本上意味着这个队列是不会拒绝任务的。同时由于两个线程数目是相等的,因此这个线程池大小固定,不会随着线程的变化而变化。
- newSingleThreadExecutor
/**
* Creates an Executor that uses a single worker thread operating
* off an unbounded queue. (Note however that if this single
* thread terminates due to a failure during execution prior to
* shutdown, a new one will take its place if needed to execute
* subsequent tasks.) Tasks are guaranteed to execute
* sequentially, and no more than one task will be active at any
* given time. Unlike the otherwise equivalent
* {@code newFixedThreadPool(1)} the returned executor is
* guaranteed not to be reconfigurable to use additional threads.
*
* @return the newly created single-threaded Executor
*/
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
和上面的线程池类似,只不过是退化成核心线程数和最大线程数都是1。也就是只能有一个线程运行。多于的任务会放置在无界队列中等待。
- newCachedThreadPool
/**
* Creates a thread pool that creates new threads as needed, but
* will reuse previously constructed threads when they are
* available. These pools will typically improve the performance
* of programs that execute many short-lived asynchronous tasks.
* Calls to {@code execute} will reuse previously constructed
* threads if available. If no existing thread is available, a new
* thread will be created and added to the pool. Threads that have
* not been used for sixty seconds are terminated and removed from
* the cache. Thus, a pool that remains idle for long enough will
* not consume any resources. Note that pools with similar
* properties but different details (for example, timeout parameters)
* may be created using {@link ThreadPoolExecutor} constructors.
*
* @return the newly created thread pool
*/
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
核心线程数目为0,最大线程数目为Integer.MAX_VALUE,这也就意味着当游戏呢任务进来时会直接放在队列里,但是这个队列比较特殊,它本身没有保存任务的能力,它会将任务直接提交个线程而不是保存他们。也就是说线程池会构造新的线程或者是使用空闲线程来调度任务,线程池中的线程如果贡献超过60秒就会被销毁。
线程池源码分析
- Executor是一个顶级接口,其中只包含一个execute方法。
- ExecutorService继承自Executor接口,它其中包含的比较重要的方法有shutdown、shutdownNow、submit等等。
- Executors提供了一系列的静态方法,用于返回线程池。其内部实现也是基于ThreadPoolExecutor实现,包括但不限于前面的三种。
- ThreadPoolExecutor是具体产生线程池的类,其中包括线程池的状态,工作流程等等代码。
线程池状态信息
/**
*
* The numerical order among these values matters, to allow
* ordered comparisons. The runState monotonically increases over
* time, but need not hit each state. The transitions are:
*
* RUNNING -> SHUTDOWN
* 执行shutdown()方法
* (RUNNING or SHUTDOWN) -> STOP
* 执行shutdownnow()方法
* SHUTDOWN -> TIDYING
* 队列和线程池都为空时为此状态
* STOP -> TIDYING
* 线程池为空
* TIDYING -> TERMINATED
* 线程池已被销毁
*
* Threads waiting in awaitTermination() will return when the
* state reaches TERMINATED.
*
* Detecting the transition from SHUTDOWN to TIDYING is less
* straightforward than you'd like because the queue may become
* empty after non-empty and vice versa during SHUTDOWN state, but
* we can only terminate if, after seeing that it is empty, we see
* that workerCount is 0 (which sometimes entails a recheck -- see
* below).
*/
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
//线程池使用后29为来记录线程数量
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
//-1的二进制为32个1,移位后为:11100000000000000000000000000000
private static final int RUNNING = -1 << COUNT_BITS;
//0的二进制为32个0,移位后:00000000000000000000000000000000
private static final int SHUTDOWN = 0 << COUNT_BITS;
//1的二进制为前面31个0,最后一个1,移位后为:00100000000000000000000000000000
private static final int STOP = 1 << COUNT_BITS;
//2的二进制为01000000000000000000000000000000
private static final int TIDYING = 2 << COUNT_BITS;
//3移位后01100000000000000000000000000000
private static final int TERMINATED = 3 << COUNT_BITS;
// ctl的三个方法分别用于获取线程池的状态,线程的数量,设置ctl的值
private static int runStateOf(int c) { return c & ~CAPACITY; }
private static int workerCountOf(int c) { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
线程池的状态一共有下面五种:
RUNNING: 接受任务并运行队列中的任务
SHUTDOWN: 不接受新的任务,但是会继续执行任务
STOP: 既不接受任务,也不执行任务,同时会终止正在运行的任务。
TIDYING: 所有的任务都被终止,队列中任务数目为0。
TERMINATED: 线程池被销毁
线程池excute运行过程
线程池运行过程如下
- 首先判断传入的任务是否为空,如果为空则直接抛出异常
- 如果线程池中的线程数量小于核心线程数量,创建新的线程并执行任务(也就是执行addWorker方法)。
- 如果线程数量超过核心线程数量,尝试把任务添加到任务队列中,如果此时线程池不是运行态并且成功移除任务,就执行拒绝策略。
- 如果线程池是运行态或者是移除任务失败,并且线程池中线程数量为0,添加任务。此时线程数量小于线程池最大线程状态。
- 最后如果添加线程失败,说明已经超过最大线程数目,也要执行拒绝策略。
/**
* Executes the given task sometime in the future. The task
* may execute in a new thread or in an existing pooled thread.
*
* If the task cannot be submitted for execution, either because this
* executor has been shutdown or because its capacity has been reached,
* the task is handled by the current {@code RejectedExecutionHandler}.
*
* @param command the task to execute
* @throws RejectedExecutionException at discretion of
* {@code RejectedExecutionHandler}, if the task
* cannot be accepted for execution
* @throws NullPointerException if {@code command} is null
*/
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
int c = ctl.get();//获取ctl的值
if (workerCountOf(c) < corePoolSize) {//如果线程数小于线程池核心线程数目
if (addWorker(command, true))//添加到Worker中去
return;//成功则返回
c = ctl.get();
}
//如果线程池是运行态,并且成功将任务添加到队列中
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
//再次获取线程池的状态,如果不是运行态并且成功移除任务,就执行拒绝策略
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)//否则如果线程池线程数量为0,添加任务到worker中去
addWorker(null, false);
}
//添加到队列中失败,直接执行拒绝策略
else if (!addWorker(command, false))
reject(command);
}
addWorker代码分析
- addworker过程首先要判断线程池的状态是不是允许添加线程,如果状态>=0就不允许添加了。
- 然后进行 判断线程池中线程数是否过量,过量则直接返回false,否则通过循环CAS加一,成功则继续执行,否则一直循环。
- 创建新的Worker并将任务放入,加锁并将Worker放入线程池中(其实就是一个HashSet),然后开始运行线程。如果线程启动失败,就进行失败处理。
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// 如果线程池的状态不允许添加任务,直接返回失败。
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
int wc = workerCountOf(c);
if (wc >= CAPACITY || //大于最大线程数,直接返回false
wc >= (core ? corePoolSize : maximumPoolSize))//根据是否是向核心线程池添加线程返回
return false;
if (compareAndIncrementWorkerCount(c))//加一成功则退出循环
break retry;
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
w = new Worker(firstTask);//添加任务到Worker中
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();//向线程池中添加任务要加锁
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive())
throw new IllegalThreadStateException();
workers.add(w);//其实就是向一个HashSet中添加线程
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
runWorker()方法是具体执行线程池中线程任务的方法。
- 首先获取当前Worker中的任务,并设置一个线程为当前线程。
- 任务不为空,否则从任务队列中获取任务,这里一直会循环。
- 如果线程池状态值>=1,就不会执行任务了,这时候需要中断线程。
- 最后执行任务,并做后续相关处理。
/**
*
*/
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {
w.lock();
// 如果池停止,则确保线程被中断;
// 如果没有,确保线程不会被中断。
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {//开始执行任务
beforeExecute(wt, task);
Throwable thrown = null;
try {
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;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
线程池shutdown()方法运行过程
调用这个方法只会中断那些空闲的线程,仍然会运行正在运行的线程。
- 关闭线程池首先需要获得锁,然后逐个检查线程。
- 设置线程池状态为shutDown,如果是>=0的状态直接返回。这里其实是一个循环CAS操作
- 尝试中断所有空闲线程。
/**
* Initiates an orderly shutdown in which previously submitted
* tasks are executed, but no new tasks will be accepted.
* Invocation has no additional effect if already shut down.
*
* <p>This method does not wait for previously submitted tasks to
* complete execution. Use {@link #awaitTermination awaitTermination}
* to do that.
*
* @throws SecurityException {@inheritDoc}
*/
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
//CAS 设置线程池状态
advanceRunState(SHUTDOWN);
//中断所有空闲线程。
interruptIdleWorkers();
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
tryTerminate();
}
线程池shutdownNow()方法运行过程
shutDownNow和shutDown的方法不同之处在于shutDownNow会将所有线程终止,并且所有的任务都不会执行,最后还会执行终止线程池的操作。
- 这个操作同样首先需要获取锁。
- 设置线程池状态为STOP,如果是>=1的状态直接返回。
- 中断线程池中所有的线程,清空任务队列,返回任务队列中的任务列表。
- 终止线程池,也就是把状态设置为TERMINATED
/**
* Attempts to stop all actively executing tasks, halts the
* processing of waiting tasks, and returns a list of the tasks
* that were awaiting execution. These tasks are drained (removed)
* from the task queue upon return from this method.
*
* <p>This method does not wait for actively executing tasks to
* terminate. Use {@link #awaitTermination awaitTermination} to
* do that.
*
* <p>There are no guarantees beyond best-effort attempts to stop
* processing actively executing tasks. This implementation
* cancels tasks via {@link Thread#interrupt}, so any task that
* fails to respond to interrupts may never terminate.
*
* @throws SecurityException {@inheritDoc}
*/
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(STOP);
interruptWorkers();
tasks = drainQueue();
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
参考资料:
https://blog.csdn.net/zqz_zqz/article/details/69488570
https://blog.csdn.net/programmer_at/article/details/79799267
http://ifeve.com/java-threadpool/
https://blog.csdn.net/pfnie/article/details/52781535
https://blog.csdn.net/gol_phing/article/details/49032055