我们以固定大小的线程池为例来进行分析:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
在这个构造方法里面传入了线程的最大数目,等待时间和一个阻塞队列:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
之后又调用了另外一个构造方法,传入了核心的线程数,最大线程数,线程池维护线程所允许的空闲时间,等待时间,工作队列,一个默认线程工厂和默认的处理器,我们接下来就看一下这两个默认的参数是什么:
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);
}
继承了ThreadFactory接口用来创建定制的线程
private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
另外一个默认参数其实就是丢弃策略,在线程池中一共由4中策略:
1、AbortPolicy策略
该策略直接抛出异常,阻止系统工作
2、CallerRunsPolicy策略
只要线程池未关闭,该策略直接在调用者线程中运行当前被丢弃的任务。显然这样不会真的丢弃任务,但是,调用者线程性能可能急剧下降。
3、DiscardOledestPolicy策略
丢弃最老的一个请求任务,也就是丢弃一个即将被执行的任务,并尝试再次提交当前任务。
4、DiscardPolicy策略
默默的丢弃无法处理的任务,不予任何处理。
线程池中处理任务的策略是这样的:
l 如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
2 如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。
3 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
4 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。也就是:处理任务的优先级为:核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。
当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。
我们继续看源码:
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.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
对我们传入的参数进行了验证并保存。
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
当由一个任务提交的时候首先会对的传入的任务进行验证,通过后封装成一个future,调用execute去执行:
public void execute(Runnable command) {
//对任务进行非空判断
if (command == null)
throw new NullPointerException();
//得到线程池的所有信息
int c = ctl.get();
//获得当前创建的线程的数目
if (workerCountOf(c) < corePoolSize) {
//如果小于corePoolSize,就创建一个线程来处理当前任务
if (addWorker(command, true))
return;
c = ctl.get();
}
//如果当前线程池还在运行当中,并且将当前任务放到工作队列里面成功
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
//重新检查线程池的状态,如果当前线程不在运行状态就将刚刚提交的任务利用丢弃策略处理
if (! isRunning(recheck) && remove(command))
reject(command);
//如果线程池处于运行状态但是工作的线程数目是0.就创建一个线程来工作
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
//如果当前线程的数目大约coresize但是小于maxsize,那么就新建一个线程来处理当前任务,否则执行丢弃策略
else if (!addWorker(command, false))
reject(command);
}
在这个方法里面做了这么几件事:
1、首先对当前提交的任务做非空判断
2、判断当前线程的数目是否小于coresize,如果是,就新创建一个线程来处理当前任务,否则进入3
3、判断当前工作队列是否已经满了,如果没有满,那么就将当前任务添加到工作队列里面,否则,进入4
4、判断当前线程的数目是否超过了maxsize,如果没有,就新创建一个线程来处理当前任务,否则进入5
5、按照丢弃策略来处理当前任务
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 ||
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);
//获得工人的线程
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 通过加锁操作将当前线程添加到线程池中
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
workers.add(w);
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;
}
一开始通过CAS操作增加线程池中线程的数目,成功后,由于要访问workers这个共享变量,给下面的执行加锁,在这里面主要就是创建了一个worker,将他添加到线程池中,并启动它的工作线程,下面看一下Worker这个类:
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
在工作方法中将一个新创建的现线程后worker绑定:
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;
}
我们看一下这个worker的run方法做了什么:
final void runWorker(Worker w) {
//获得当前线程
Thread wt = Thread.currentThread();
//获得任务
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock();
//允许被中断
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);
}
}
在当前线程池没有停止的情况下当前线程没有被中断的情况下,在任务执行前先做一些处理:
protected void beforeExecute(Thread t, Runnable r) { }
这是一个模板方法,接下来就是任务正式执行,执行完毕后,当前worker会从工作队列中继续获取任务:
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
//获得线程池的运行状态
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
//获得工人的数目
int wc = workerCountOf(c);
// 工人是否允许被淘汰
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
//如果满足一定的条件就会淘汰当前工人
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
//获取任务
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
在这个方法中根据用户传入的参数和当前线程的数目来判断当前线程时候允许被淘汰,如果允许被淘汰,那么如果不能在keepAliveTime内获取到任务,这个工人就会被淘汰,否则就一直阻塞到获取任务。