1. 线程池的属性
| 属性 | 作用 |
|---|
| ctl | 用来表示线程池的状态和数量,高三位表示线程池的状态,其余的位数表示线程池的数量 |
| RUNNING | ctl的高三位,补码为111,表示-1吗, 线程池处于此状态时可以接受任务,可以执行任务队列的任务 |
| SHUTDOWN | ctl的高三位,补码是000,表示0,线程池处于此状态时不可以接收任务,但可以执行任务队列的任务 |
| STOP | STOP的状态码是001,表示1,线程池处于此状态时不可以接受任务,还会清空任务队列,以及中断正在执行的任务 |
| TIDYING | TYDYING的状态码是002,表示2,所有的任务都执行完毕后,(同时也涵盖了阻塞队列中的任务),当前线程池中的活动都线程数量降为0,将会调用terminated方法。 |
| TERMINATED | TERMINTED状态码是003,表示3,当线程池执行了terminated的方法后,状态会变成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;
private static int runStateOf(int c) { return c & ~CAPACITY; }
private static int workerCountOf(int c) { return c & CAPACITY; }
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;
2. 线程池的参数
| 参数 | 意义 |
|---|
| corePoolSize | 核心线程数,当加入一个任务交给线程池执行时,如果线程池中线程的数量没有达到核心线程数,那么就会新建一个线程执行此任务 |
| maximumPoolSize | 最大线程数,当线程池中的线程数量大于此值时,不会再增加线程,当线程池的线程数量处于核心线程和最大线程数之间时,就会将新添加的任务加入到任务队列 |
| workQueue | 任务队列,用于存储任务Runnable |
| keepAliveTime | 非核心线程的空闲存活时间,也就是当线程数大于核心线程数时,如果某个线程空闲了keepAliveTime这么久,就会去除此线程 |
| unit | keepAliveTime的单位 |
| threadFactory | 线程工厂,利用指定的工厂创建线程,可以方便追踪我们的线程 |
| handler | 拒绝策略,当线程池中线程数大于最大线程数值时或者是线程池处于非RUNNING时,会拒绝当前加入的任务,调用handler.reject() |
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler);
3. execute方法
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < 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);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
addWorker方法---创建线程并启动
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (
int c = ctl.get()
// 获取线程池的状态
int rs = runStateOf(c)
//这个判断条件可以拆分成(rs>=SHUTDOWN && (rs!=SHUTDOWN || firstTask!=null || workQueue.isEmpty())),任意条件成立就不会添加新线程并且返回
// ①:rs!=SHUTDOWN为真,那么rs>SHUTDOWN,不能接收新任务
// ②:rs!=SHUTDOWN为假,那么rs==SHUTDOWN且添加的任务不为null时,由于在SHUTDOWN状态不能处理新任务,所以直接返回,当firstTask为null时创建的线程会处理任务队列(如果任务队列不为空)的任务,所以firstTask为null不会返回
// ③:rs!=SHUTDOWN为假,那么rs==SHUTDOWN且任务队列为空,且firstTask为null,由于此时任务队列为空,创建的新线程没有作用,所以直接返回
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false
// 自循环创建线程
for (
// 获取线程池中线程数量
int wc = workerCountOf(c)
// 如果创建的是核心线程,那么wc>核心线程数直接返回
// 如果创建的是非核心线程,那么wc>最大线程数直接返回
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false
// 利用cas增加线程池的线程数量,成功就退出循环
if (compareAndIncrementWorkerCount(c))
break retry
c = ctl.get()
// 如果状态变为了非RUNNING,就进入上外循坏重新判断
if (runStateOf(c) != rs)
continue retry
// else CAS failed due to workerCount change
}
}
// 线程是否启动的标志变量
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())
// 如果线程池的状态为RUNNING
// 或者是线程池的状态为SHUTDOWN且firstTask为null
// 为什么要多次检查线程池的状态呢,因为线程池的操作环境也可能是多线程的,不能保证线程池的状态一直不变
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
// 如果线程已经start了,抛异常
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException()
// 添加线程到workers中,workers是一个hashset
// 所以需要加锁
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
}
总结一下,什么时候添加新线程会失败
- 当线程池的状态大于等于STOP时
- 当线程池的状态是shutdown时并且firstTask不为空,由于shutdown时的线程池不能处理新的任务,所以添加worker失败
- 当线程池的状态是shutdown时并且任务队列为null时,shutdown状态的线程池可以执行任务队列中的任务,但是任务队列已经没有任务了,所以不能添加worker
runWorkers方法----线程池执行任务
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) {
// worker实现了aqs,并且是不可重入的
// 也就是说当worker加了锁就代表worker正在执行任务
// 当线程池shutdown时只需要尝试使用worker获取锁trylock,如果成功了就代表worker没有运行
// 如果失败了就代表worker运行着
w.lock()
// 当线程池的状态是stop,就中断线程,仅仅设置中断标记
// 具体的中断还是看被中断的线程是怎么处理的
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
} catch (Error x) {
thrown = x
} catch (Throwable x) {
thrown = x
} finally {
afterExecute(task, thrown)
}
} finally {
task = null
// 处理任务统计
w.completedTasks++
// 释放锁
w.unlock()
}
}
completedAbruptly = false
} finally {
// 退出while循坏就会将worker kill掉,具体的操作还要看getTask方法
processWorkerExit(w, completedAbruptly)
}
}
总结一下,为什么执行任务时worker需要加锁呢?
- 加锁之后就代表了此worker正在运行,如果线程池在shutdown时,尝试获取worker的锁失败,证明此worker正在运行,就不会对worker进行interrupt;反之worker是空闲的,对空闲的worker进行interrupt
getTask方法----线程池获取任务队列和清除空闲线程
private Runnable getTask() {
boolean timedOut = false
for (
int c = ctl.get()
int rs = runStateOf(c)
// 当线程池是SHUTDOWN时并且队列为null,因为SHUTDOWN可以处理任务队列中的任务,所以只有
// 任务队列为空时就返回一个null,runworker方法就会退出while循环,进入删除worker阶段
// 当线程池是STOP往上时,也进入删除worker阶段
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount()
return null
}
int wc = workerCountOf(c)
// allowCoreThreadTimeOut代表核心线程是否支持超时,默认为false
// 当线程池线程数wc>corePoolSize时,timed为true
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize
// 如果线程池线程数大于maximumPoolSize(什么时候会出现这种情况呢?),并且任务队列为空
// 如果线程池线程数大于核心线程数,并且timedOut(当前线程已经等待了keepAliveTime的时间)
// 如果此时任务队列没有任务,就需要将当前线程清除,返回null
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null
continue
}
try {
// 如果线程池线程数大于核心线程数值,那就从任务队列中获取任务,当keepAliveTime时间
// 内没有获取到任务,就设置timedOut = true,对应上方的代码
// 如果线程池线程数小于核心线程数值,那就一直等待任务队列的任务,直到任务队列中
// 新添加任务为止
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take()
if (r != null)
return r
timedOut = true
} catch (InterruptedException retry) {
timedOut = false
}
}
}
总结一下,线程池什么时候会回收线程呢?
- 当线程池的状态为stop时
- 当线程池的状态为SHUTDOWN时并且任务队列没有任务
- 当线程池的线程数量大于核心线程数时并且当前线程在等待了keepAliveTime时间还没有获取到任务时
shutdown方法----中断空闲线程
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(SHUTDOWN);
interruptIdleWorkers();
onShutdown();
} finally {
mainLock.unlock();
}
tryTerminate();
}
private void interruptIdleWorkers() {
interruptIdleWorkers(false);
}
private void interruptIdleWorkers(boolean onlyOne) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers) {
Thread t = w.thread;
if (!t.isInterrupted() && w.tryLock()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
if (onlyOne)
break;
}
} finally {
mainLock.unlock();
}
}
final void tryTerminate() {
for (;;) {
int c = ctl.get();
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
return;
if (workerCountOf(c) != 0) {
interruptIdleWorkers(ONLY_ONE);
return;
}
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
terminated();
} finally {
ctl.set(ctlOf(TERMINATED, 0));
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
}
}
总结一下,shutdown做了些什么
shutdownNow方法
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;
}
private void interruptWorkers() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers)
w.interruptIfStarted();
} finally {
mainLock.unlock();
}
}
void interruptIfStarted() {
Thread t;
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
总结一下,shutdownNow做了些什么
- 中断所有线程
- 清除掉任务队列中的任务
- 设置线程池状态
测试shutdown和shutdownNow方法
class TestRunnableNoInterrupt implements Runnable {
@Override
public void run() {
while(true) {
System.out.println("我会一直运行,不被线程池中断");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("我不想中断");
}
}
}
}
class TestRunnableCanInterrupt implements Runnable{
@Override
public void run() {
while (true) {
System.out.println("我会被线程池中断");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
break;
}
}
System.out.println("我被中断了");
}
}
public class ThreadPoolTest {
public static final ThreadPoolExecutor THREAD_POOL =
new ThreadPoolExecutor(2,5,10, TimeUnit.SECONDS,new ArrayBlockingQueue<>(2));
public static void main(String[] args) throws InterruptedException {
THREAD_POOL.execute(new TestRunnableCanInterrupt());
THREAD_POOL.execute(new TestRunnableNoInterrupt());
Thread.sleep(500);
THREAD_POOL.shutdown();
}
}
- main方法中使用的是shutdown方法中断线程,由于shutdown只会中断(interrupt方法)空闲的线程,所以上面两个线程会一直输出
- 如果把shutdown改为shutdownNow方法,就会去中断所有的线程,不管这个线程是否在运行。上方的两个线程都调用了sleep,如果此时有线程调用他们的interrupt方法,sleep方法会抛出InterruptedException异常。线程池在shutdownNow的时候会暴力的调用所有线程的interrupt方法,所以这两个线程就会捕捉到异常,一个线程打印“我不想中断”,另外一个线程退出循环
- 总结来说,就是shutdown和shutdownNow都不一定能把线程停止,这要看线程是怎么实现run方法的;
- shutdown和shutdownNow的区别就是shutdown只会去调用空闲线程的interrupt方法,而shutdownNow不管当前线程是否在运行,直接调用interrupt方法,并且清空任务队列中的runnable对象,非常暴力
如何设置线程池中的线程数量
埋个坑