常用的线程池
new SingleThreadExecutor:创建一个单线程的线程池,此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
new FixedThreadPool:创建固定大小的线程池,每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。
new CachedThreadPool:创建一个可缓存的线程池,此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
new ScheduledThreadPool:创建一个大小无限的线程池,此线程池支持定时以及周期性执行任务的求。
线程池的七个参数:核心线程数,最大线程数,活跃时间,时间单位,任务队列,线程工厂,拒绝策略;
public ThreadPoolExecutor(int corePoolSize, //核心线程数
int maximumPoolSize, //最大线程数
long keepAliveTime,//当活跃线程大于核心线程时,空闲的多余的线程最大存活时间
TimeUnit unit, //存活的时间单位
BlockingQueue<Runnable> workQueue, //存放任务的队列
ThreadFactory threadFactory, //线程工厂
RejectedExecutionHandler handler) //超出线程和队列的容量的任务的处理程序
拒绝策略有哪些:
AbortPolicy:直接丢弃任务,抛出异常,这是默认策略;
CallerRunsPolicy:只用调用者所在的线程来处理任务
DiscardOldestPolicy:丢弃等待队列中最旧的任务,并执行当前任务
DiscardPolicy:直接丢弃任务,也不抛出异常
ThreadLocal原理
ThreadLocal相当于线程本地变量,每个线程都有一个本地变量,线程之间是隔离的,所以是线程安全的;
它有一个内部类ThreadLocalMap,ThreadLocalMap包含了一个Entry数组,entry本身是一个弱引用,弱引用在执行下一次gc的时候进行回收,不会存在内存泄漏问题;
源码方法讲解
execute方法
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
/**
*如果当前活动的线程数小于corePoolSize,
*则新建一个线程放入线程池中,并把该任务放到线程中
*/
if (workerCountOf(c) < corePoolSize) {
/**
* addWorker中的第二个参数表示限制添加线程的数量
* 是根据据corePoolSize 来判断还是maximumPoolSize来判断;
* 如果是ture,根据corePoolSize判断
* 如果是false,根据maximumPoolSize判断
*/
if (addWorker(command, true))
return;
//如果添加失败,则重新获取ctl值
c = ctl.get();
}
/**
* 如果线程池是Running状态,并且任务添加到队列中
*/
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
/**
* 再次判断线程池的状态,如果不是运行状态,由于之前已经把
* command添加到阻塞队列中,这时候需要从队列中移除command;
* 通过handler使用拒绝策略对该任务进行处理,整个方法返回
*/
if (! isRunning(recheck) && remove(command))
reject(command);
/**
* 获取线程池中的有效线程数,如果数量是0,则执行addWorker方法;
* 第一个参数为null,表示在线程池中创建一个线程,但不去启动
* 第二个参数为false,将线程池的线程数量的上限设置为maximumPoolSize,
* 添加线程时根据maximumPoolSize来判断
*/
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
/**
* 执行到这里,有两种情况:
* 1、线程池的状态不是RUNNING;
* 2、线程池状态是RUNNING,但是workerCount >= corePoolSize,
* workerQueue已满这个时候,再次调用addWorker方法,第二个参数传false,
* 将线程池的有限线程数量的上限设置为maximumPoolSize;
* 如果失败则执行拒绝策略;
*/
else if (!addWorker(command, false))
reject(command);
}
在执行execute()方法时如果状态一直是RUNNING时,的执行过程如下:
判断核心线程是否都在执行任务,如果有核心线程空闲或者还没有被创建,则起一个线程执行任务,否则进入下面的流程
判断任务队列是否满了,如果没有满则进队列等待,否则进下一流程;
判断是否达到最大线程数,如果没有则创建线程执行任务,否则进下一流程
任务队列跟最大线程都满了之后,进行拒绝策略程序处理
addWorker(null, false); 在没有任务传入的时候也需要创建一个线程,保持线程在RUNNING状态必须要有一个线程来执行任务,所以workerCountOf(recheck) == 0时执行addWorker(null, false);第二个参数设置为false,表示设置上限为最大线程数,有些线程池可以设置核心线程数为0,但是线程池里面也有活跃线程
addWorker方法
主要作用是在线程池中创建一个新的线程并执行,firstTask参数用于指定新增的线程执行的第一个任务,新增线程时,core参数为true时判断活动线程数是否少于corePoolSize ,false活动的线程数是否少于maximumPoolSize
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
/**
* 由于线程执行过程中,各种情况都有可能处于,
*通过自旋的方式来保证worker的增加;
*/
for (;;) {
int c = ctl.get();
//获取线程运行状态
int rs = runStateOf(c);
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN && //表示不再接收新的任务
! (rs == SHUTDOWN && //表示关闭状态,不再接收提交的任务,
//但却可以继续处理阻塞队列中已经保存的任务;
firstTask == null && //fisrtTask为空
! workQueue.isEmpty()))
return false;
for (;;) {
//获取线程数
int wc = workerCountOf(c);
/**
* ctl的低29位的最大值,则返回false;
* 是否大于核心线程数或最大线程数
*/
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
/**
* 通过CAS原子的方式来增加线程数量;
* 如果成功,则跳出第一个for循环;
*/
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
//如果运行状态不等于rs,说明线程状态改变,返回第一for继续执行
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 {
//获取worker对象
w = new Worker(firstTask);
//创建线程
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());
/**
* 线程池的状态小于SHUTDOWN,表示线程池处于RUNNING状态;
* 如果rs是RUNNING状态或rs是SHUTDOWN状态并且firstTask为null,向线程池中添加线程;
* 因为在SHUTDOWN状态时不会再添加新的任务,但还是处理workQueue中的任务;
*/
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;
}
Worker类
线程池中的每一个对象被封装成一个Worker对象,ThreadPool维护的就是一组Worker对象。Worker类继承了AQS,并实现了Runnable接口,其中包含了两个重要属性:firstTask用来保存传入的任务,thread是在调用构造方法是通过ThreadFactory来创建的线程,是用来处理任务的线程。
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable {
final Thread thread;
Runnable firstTask;
volatile long completedTasks;
Worker(Runnable firstTask) {
/**
* 把state设置为-1,,阻止中断直到调用runWorker方法;
* AQS默认state是0,如果刚创建一个Worker对象,
* 还没有执行任务时,这时候不应该被中断
*/
setState(-1);
this.firstTask = firstTask;
/**
* 创建一个线程,newThread方法传入的参数是this,
* 因为Worker本身继承了Runnable接口,也就是一个线程;
* 所以一个Worker对象在启动的时候会调用Worker类中run方法
*/
this.thread = getThreadFactory().newThread(this);
}
}
Worker类继承了AQS,使用AQS来实现独占锁的功能。为什么不使用ReentrantLock来实现?可以看到tryAcquire方法,他是不允许重入的,而ReentrantLock是允许可重入的:
1、lock方法一旦获取独占锁,表示当前线程正在执行任务中;
2、如果正在执行任务,则不应该中断线程;
3、如果该线程现在不是独占锁的状态,也就是空闲状态,说明它没有处理任务,这时可以对该线程进行中断;
4、线程池中执行shutdown方法或tryTerminate方法时会调用interruptIdleWorkers方法来中断空闲线程,interruptIdleWorkers方法会使用tryLock方法来判断线程池中的线程是否是空闲状态;
5、之所以设置为不可重入的,是因为在任务调用setCorePoolSize这类线程池控制的方法时,不会中断正在运行的线程所以,Worker继承自AQS,用于判断线程是否空闲以及是否处于被中断。
protected boolean tryAcquire(int unused) {
/**
* cas修改state,不可重入;
* state根据0来判断,所以Worker构造方法中讲state置为-1是
* 为了禁止在执行任务前对线程进行中断;因此,在runWorker方法中会先
* 调用Worker对象的unlock方法将state设置为0
*/
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
runWorker方法
在Worker类中的run方法调用了runWorker方法来执行任务
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
//获取任务
Runnable task = w.firstTask;
w.firstTask = null;
//允许中断
w.unlock();
//是否因异常退出循环
boolean completedAbruptly = true;
try {
//如果task为空,则通过getTask来获取任务
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和afterExecute是留给子类来实现的
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 {
//会对completedAbruptly进行判断,表示在执行过程中是否出现异常
processWorkerExit(w, completedAbruptly);
}
}
runWorker方法的执行过程:
1、while循环不断地通过getTask方法来获取任务;
2、getTask方法从阻塞队列中获取任务;
3、如果线程池正在停止,那么要保证当前线程处于中断状态, 否则要保证当前线程不是中断状态;
4、调用task.run()执行任务;
5、如果task为null则会跳出循环,执行processWorkerExit方法;
6、runWorker方法执行完毕,也代表着Worker中的run方法执行完毕,销毁线程。
getTask方法
从阻塞队列中获取任务
private Runnable getTask() {
//timejavaout变量的值表示上次从阻塞队列中获取任务是否超时
boolean timedOut = false;
for (; ; ) {
int c = ctl.get();
int rs = runStateOf(c);
/**
* 如果rs >= SHUTDOWN,表示线程池非RUNNING状态,需要再次判断:
* 1、rs >= STOP ,线程池是否正在STOP
* 2、阻塞队列是否为空
* 满足上述条件之一,则将workCount减一,并返回null;
* 因为如果当前线程池的状态处于STOP及以上或队列为空,不能从阻塞队列中获取任务;
*/
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
/**
* timed变量用于判断是否需要进行超时控制;
* allowCoreThreadTimeOut默认是false,也就是核心线程不允许进行超时;
* wc > corePoolSize,表示当前线程数大于核心线程数量;
* 对于超过核心线程数量的这些线程,需要进行超时控制;
*/
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
/**
* wc > maximumPoolSize的情况是因为可能在此方法执行阶段同时执行了 setMaximumPoolSize方法;
* timed && timedOut 如果为true,表示当前操作需要进行超时控制,并且上次从阻塞队列中获取任务发生了超时;
* 接下来判断,如果有效咸亨数量大于1,或者workQueue为空,那么将尝试workCount减1;
* 如果减1失败,则返回重试;
* 如果wc==1时,也就说明当前线程是线程池中的唯一线程了;
*/
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
/**
* timed为trure,则通过workQueue的poll方法进行超时控制,如果在keepAliveTime时间内没有获取任务,则返回null;
* 否则通过take方法,如果队列为空,则take方法会阻塞直到队列中不为空;
*/
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
//如果r==null,说明已经超时了,timedOut = true;
timedOut = true;
} catch (InterruptedException retry) {
//如果获取任务时当前线程发生了中断,则将timedOut = false;
timedOut = false;
}
}
}
注意:第二个if判断,目的是为了控制线程池的有效线程数量。有上文分析得到,在execute方法时,如果当前线程池的线程数量超过coolPoolSize且小于maxmumPoolSize,并且阻塞队列已满时,则可以通过增加工作线程。但是如果工作线程在超时时间内没有获取到任务,timeOut=true,说明workQueue为空,也就说当前线程池不需要那么多线程来执行任务了,可以把多于的corePoolSize数量的线程销毁掉,保证线程数量在corePoolSize即可。
什么时候会销毁线程?当然是runWorker方法执行完后,也就是Worker中的run方法执行完后,由JVM自动回收。
processWorkerExit
方法执行后销毁线程
private void processWorkerExit(Worker w, boolean completedAbruptly) {
/**
* 如果completedAbruptly为true,则说明线程执行时出现异常,需要将workerCount数量减一
* 如果completedAbruptly为false,说明在getTask方法中已经对workerCount进行减一,这里不用再减
*/
if (completedAbruptly)
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//统计完成的任务数
completedTaskCount += w.completedTasks;
//从workers中移除,也就表示从线程池中移除一个工作线程
workers.remove(w);
} finally {
mainLock.unlock();
}
//钩子函数,根据线程池的状态来判断是否结束线程池
tryTerminate();
int c = ctl.get();
/**
* 当前线程是RUNNING或SHUTDOWN时,如果worker是异常结束,那么会直接addWorker;
* 如果allowCoreThreadTimeOut=true,那么等待队列有任务,至少保留一个worker;
* 如果allowCoreThreadTimeOut=false,workerCount少于coolPoolSize
*/
if (runStateLessThan(c, STOP)) {
if (!completedAbruptly) {
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
if (min == 0 && !workQueue.isEmpty())
min = 1;
if (workerCountOf(c) >= min)
return; // replacement not needed
}
addWorker(null, false);
}
}
工作线程的生命周期:从execute方法开始,Worker使用ThreadFactory创建新的工作线程,runWorker通过getTask获取任务,然后执行任务,如果getTask返回null,进入processWorkerExit,线程结束。