线程池出现的原因
为什么需要线程池呢?我们直接通过new Thread创建线程不就好了吗?
Thread对象和普通的对象不一样,普通的对象在JVM中划分一块内存区域将数据存到里面就行了,而Thread需要调用操作系统的API来为Thread分配资源,需要从用户态切换到内核态,十分消耗资源,所以Thead是一个重量级对象,要避免线程频繁的创建和销毁,所以就引入了线程池!
线程池实例
//任务队列
BlockingQueue queue = new LinkedBlockingQueue(10);
ThreadPoolExecutor executor = new ThreadPoolExecutor(4,10,2,TimeUnit.SECONDS,queue);
executor.execute(new Runnable() {
@Override
public void run() {
System.out.println("test");
}
});
源码解析
主要属性
//线程池的运行信息标志,前3位用来表示线程池的运行状态,后29位用来记录当前线程的个数
//初始值为111 0 0000 0000 0000 0000 0000 0000 0000,代表运行状态,线程个数为0
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
//用来记录线程个数的位数 32 - 3 = 29
private static final int COUNT_BITS = Integer.SIZE - 3;
//容纳线程的最大容量,000 1 1111 1111 1111 1111 1111 1111 1111
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
//线程池的五个状态,RUNNING、SHUTDOWN、STOP、TIDYING、TERMINATED值依次增大
//running 111 0 0000 0000 0000 0000 0000 0000 0000
private static final int RUNNING = -1 << COUNT_BITS;
//关闭 000 0 0000 0000 0000 0000 0000 0000 0000
private static final int SHUTDOWN = 0 << COUNT_BITS;
//停止 001 0 0000 0000 0000 0000 0000 0000 0000
private static final int STOP = 1 << COUNT_BITS;
// 010 0 0000 0000 0000 0000 0000 0000 0000
private static final int TIDYING = 2 << COUNT_BITS;
// 011 0 0000 0000 0000 0000 0000 0000 0000
private static final int TERMINATED = 3 << COUNT_BITS;
//用来获取线程运行状态 c & 111 0 0000 0000 0000 0000 0000 0000 0000
private static int runStateOf(int c) { return c & ~CAPACITY; }
//用来获取线程个数 c & 000 1 1111 1111 1111 1111 1111 1111 1111
private static int workerCountOf(int c) { return c & CAPACITY; }
//用来初始化ctl
private static int ctlOf(int rs, int wc) { return rs | wc; }
//工作线程集合
private final HashSet<Worker> workers = new HashSet<Worker>();
流程
初始化
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
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;
}
1、corePoolSize:核心线程个数,
2、maximumPoolSize:最大线程个数
3、workQueue:待执行的任务队列,我们最好使用有界队列,如果设置无界队列,很容易导致OOM。
4、keepAliveTime:线程的最大空闲时间,如果超过keepAliveTime个TimeUnit时间内没有执行任务,非核心线程就会被回收。如果想要回收核心线程的话,需要设置allowCoreThreadTimeOut(true)。
5、TimeUnit:keepAliveTime的时间单位
6、threadFactory:创建线程的工厂
7、hanlder:当线程池不够用的时候的拒绝策略,有四种策略,后面我们结合源码详细讲。
线程池中的线程被分为两类:
1、核心线程:一般不会被回收
2、非核心线程:如果连续keepAliveiTime个TimeUnit时间内没有执行任务,就会被回收。
2、execute()
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();//获取当前线程池信息
//workerCountOf用来获取线程个数,如果线程个数小于corePoolSize,就增加线程
//线程池中的线程是懒加载的,当用到的时候才会加载,初始化的时候不会加载。
if (workerCountOf(c) < corePoolSize) {
//添加线程,command代表任务,true代表是否是核心线程
if (addWorker(command, true))
return;
c = ctl.get();
}
//如果当前线程数量>=corePoolSize,并且线程池处于运行状态就将任务加入wordQueue中
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
//再次检测线程池状态,如果线程池不运行了,就将command从wordQueue中移除
if (! isRunning(recheck) && remove(command))
//返回拒绝策略
reject(command);
//如果当前工作线程数量为0,才会加入一个非核心线程
else if (workerCountOf(recheck) == 0)
//加入非核心线程
addWorker(null, false);
}
//如果加入workQueue失败(队列满了),才会加入一个非核心线程,如果加入非核心线程失败,就执行拒绝策略
else if (!addWorker(command, false))
reject(command);
}
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
//获取线程运行状态
int rs = runStateOf(c);
//如果线程不是运行状态 或者 firstTask== null 或者 workQueue为空,返回false
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
//获取线程个数
int wc = workerCountOf(c);
//1、如果wc大于最大的线程个数CAPACITY,返回false
//2、如果是核心线程,大于等于corePoolSize,返回false
//3、如果是非核心线程,大于等于maximumPoolSize,返回false
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
//将c++,也就是线程个数+1
if (compareAndIncrementWorkerCount(c))
//退出循环
break retry;
//获取c
c = ctl.get(); // Re-read ctl
//如果线程池运行状态发生变化,重新循环
if (runStateOf(c) != rs)
continue retry;
}
}
//线程是否开始工作
boolean workerStarted = false;
//线程是否加入到queue中
boolean workerAdded = false;
//线程
Worker w = null;
try {
//初始化Worker线程
w = new Worker(firstTask);
//Worker(Runnable firstTask) {
// setState(-1);//设置AQS的state,Worker是继承AQS的。
// this.firstTask = firstTask;//Thread的第一个任务,优先执行
// worker的线程为getThreadFactory的新建线程
// this.thread = getThreadFactory().newThread(this);
//}
final Thread t = w.thread;
if (t != null) {
//mainLock是ThreadPoolExecutor中的一个属性,Worker是一个内部类
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//获取线程状态
int rs = runStateOf(ctl.get());
//线程池处于运行状态,或者线程池处于关闭状态,并且firstTask = null
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
//如果线程还活着,抛出异常
if (t.isAlive())
throw new IllegalThreadStateException();
//工作线程集合中加入w
workers.add(w);
int s = workers.size();
//更新largestPoolSize的值
if (s > largestPoolSize)
largestPoolSize = s;
//加入成功
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
//启动worker
t.start();
workerStarted = true;
}
}
} finally {
//加入异常
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
通过上述代码我们看出execute的流程
1、如果线程个数小于corePoolSize,就加入一个核心线程Worker,并将其command设置为worker的firstTask,优先执行。
2、如果线程个数大于等于corePoolSize,如果线程池处于运行状态,就优先将command加入到workerQueue中。
如果加入workerQueue中失败,这时很有可能是workerQueue队列满了,这时会尝试加入一个非核心线程worker,将worker的firstTaks设置为command。
3、在加入worker的时候,如果线程不处于运行状态,或者workerQueue为空,或者如果加入核心线程,线程个数大于corePoolSize,如果是非核心线程,线程个数大于maximumPoolSize都会导致加入失败。
接下来我们说下Worker的run方法
public void run() {
runWorker(this);
}
final void runWorker(Worker w) {
//获取w的Thread
Thread wt = Thread.currentThread()
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock();
boolean completedAbruptly = true;
try {
//从这里可以看出优先执行firstTask
//getTask()是从workQueue中获取任务
while (task != null || (task = getTask()) != null) {
w.lock();
//如果线程状态为STOP、TIDYING、TERMINATE或者线程被中断的话,就线程自我中断
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
//protected void beforeExecute(Thread t, Runnable r) { }
//在任务执行前调用的方法,用户可以继承ThreadPoolExecutor自定义实现,比如日志什么的
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 {
//任务执行之后调用的方法,用户也是可以继承ThreadPoolExecutor自定义实现的,比如日志什么的
afterExecute(task, thrown);
}
} finally {
task = null;
//worker完成的任务数++;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
//从workerQueue中获取任务
private Runnable getTask() {
boolean timedOut = false; // 获取task是否超时
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
//如果线程处于SHUTDOWN、STOP、TERMINATE、TIDYING或者workQueue为空,
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();//将workcount--
//返回了null,worker的run方法也会调用结束,对应的worker也会停止
return null;
}
//工作线程个数
int wc = workerCountOf(c);
//allowCoreThreadTimeOut为true代表着核心线程如果长时间不执行任务,也会被终止
//线程是否会被回收,如果设置了allowCoreThreadTimeOut或者线程个数大于corePoolSize
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
//如果线程个数大于maximumPoolSize,或者线程个数大于1,但是workQueue为空,就会减少线程的个数
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
//worker的个数--
if (compareAndDecrementWorkerCount(c))
return null;返回了null,worker的run方法也会调用结束,对应的worker也会停止
continue;
}
try {
//如果设置了可回收,就以keepAliveTime为超时时间申请task
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
//如果超时了或者workQueue为空,timedOut为true,在下一轮循环中会如果timed = true的话会返回null,将线程回收
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
通过以上代码,我们可以看出Worker的工作流程
1、worker设置了一个while循环,循环的条件是firstTaks!=null,或者能够从workQueue中获取到task
2、如果firstTask不为null,就会优先执行firstTask。
3、如果firstTask为null,就会到workQueue中获取task
4、如果获取不到workQueue,也就是task == null,线程worker就会停止,在返回null之前会利用CAS将线程个数-1.
以下情况会返回null
1、如果允许回收核心线程allowCoreThreadTimeOut(true)并且核心线程获取task超时,并且workQueue为空
2、如果线程个数>maxPoolSize
3、如果线程池状态为STOP、TERMINATE、TIDYING或者线程池状态为SHUTDOWN并且workQueue为空。
拒绝策略
当调用execute()方法失败的时候,就会发出拒绝策略。
拒绝策略的接口为RejectedExecutionHandler,其有四个子类,对应着四种不同的处理方法,在初始化的时候,我们可以指定四个中的任意一个。
1、DiscardOldestPolicy
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
从上述代码看出,DiscardOldestPolicy会将最老的任务从workQueue中出队,然后继续执行当前的command
2、DiscardPolicy
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
DiscardPolicy就是将这个任务直接丢弃,什么都不干
3、AbortPolicy
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
AbortPolicy的做法是抛出异常。
4、CallerRunsPolicy
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
CallerRunsPolicy的做法是在调用execute()的线程中执行。
总结
ThreadPoolExecutor的核心就是Thread集合、BlockingQueue。Thread会从BlockingQueue中不断拉取任务Runnable然后执行,相当于一个生产者和消费者模型。