スレッドプールシリーズ:
- 【JUCソースコード】スレッドプール:ThreadPoolExecutor(1)継承関係分析
- [JUCソースコード]スレッドプール:ThreadPoolExecutor(2)の基礎となる構造解析
- [JUCソースコード]スレッドプール:ThreadPoolExecutor(3)ワーカーデザインのアイデアとソースコード分析
- [JUCソースコード]スレッドプール:タスク実行プロセスのThreadPoolExecutor(4)ソースコード分析
- [JUCソースコード]スレッドプール:ThreadPoolExecutor(5)の概要
- [JUCソースコード]スレッドプール:スレッドプール&Excutorsを作成するためのパラメーター設定のアイデア
- [JUCソースコード]スレッドプール:ThreadPoolに関するいくつかの質問
1.execute()
エントランス、実行戦略を選択し、次の3つの状況に分けます。
- ケース1:ワーカースレッド<コアの数、タスクを実行するためのスレッドを作成する
- ケース2:ワーカースレッド> =コアの数であり、タスクキューがいっぱいではない場合、タスクキューに参加します(コアスレッドが実行されるのを待機します)
- スレッドプールが異常です。現在のタスクを削除してください
- 制限状況:使用可能なスレッドはエンキュー時にリサイクルされるだけで、タスクのない新しいスレッドが作成されます
- 状況3:タスクキューがいっぱいです
- キューがいっぱいです&&スレッド数<maxSize:タスクを処理するための新しいスレッドを作成します
- キューがいっぱいです&&スレッド数> = maxSize:RejectedExecutionHandlerクラスを使用してリクエストを拒否します
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get(); // 获取 ctl
// 情况一:工作的线程小于核心线程数,创建新的线程,成功返回,失败不抛异常
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
// 由于 addWorker -> runWorker -> getTask,所以线程池状态可能发生变化
c = ctl.get();
}
// 情况二:工作的线程大于等于核心线程数且任务队列没满
// 注:isRunning是校验线程池状态是否正常。另外,offer不阻塞而是返回t/f
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
// 如果线程池状态异常 尝试从队列中移除任务,可以移除的话就拒绝掉任务
if (!isRunning(recheck) && remove(command))
reject(command);
// 发现可运行的线程数是 0,就初始化一个线程,这里是个极限情况,入队的时候,突然发现可用线程都被回收了
else if (workerCountOf(recheck) == 0)
// Runnable是空的,不会影响新增线程,但是线程在 start 的时候不会运行
// Thread.run() 里面有判断
addWorker(null, false);
}
// 情况三:队列满了,开启线程到 maxSize,如果失败直接拒绝(这段逻辑可以在addWorker方法中看到)
else if (!addWorker(command, false))
reject(command);
}
2.addWorker()
ワーカーを作成し、ワーカーが正常に開始されたかどうかを返します。一般的なプロセスは次のとおりです。
- スレッドプールステータスの検証
- 失敗した場合はfalseを返します。2つの理由があります:
- 異常なスレッドプールステータス:SHUTDOWN、STOP、TIDYING、TERMINALED
- ワーカースレッド数のオーバーフロー:スレッド数> =容量、またはcoreThreadを使用する場合は、スレッド数> = coreSizeまたはmaxSize
- 成功:CASはworkCountを1つ増やします
- 失敗した場合はfalseを返します。2つの理由があります:
- ワーカーを作成する
- 2つの識別変数を作成します:workerAdded、workerStarted
- ワーカーを構築します。構築中にnewThreadメソッドを使用して新しいスレッドが作成されます
- ロックし、新しく作成したワーカーを、ワーカーを管理するコンテナー(セット)に追加します。ロックは同時実行中のスレッドセーフを保証します
- ワーカーでスレッドを開始します。呼び出しロジックは次のとおりです。Thread#start()-> Worker#run()-> runWorker()
// firstTask 不为空可以直接执行,为空执行不了,Thread.run()方法有判断,Runnable为空不执行
// core 为 true 表示线程最大新增个数是 coresize,false 表示最大新增个数是 maxsize
private boolean addWorker(Runnable firstTask, boolean core) {
// break retry 跳到retry处,且不再进入循环
// continue retry 跳到retry处,且再次进入循环
retry:
--------------------------------------------------------------------------------------------------------------
// 1.先是各种状态的校验
for (;;) {
int c = ctl.get();
int rs = runStateOf(c); // 获取线程池状态
// 1.1 校验线程池状态,rs>=0:SHUTDOWN,STOP,TIDYING,TERMINALED
if (rs >= SHUTDOWN && ! (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty()))
return false;
for (;;) {
int wc = workerCountOf(c); // 得到当前工作线程数,即worker数
// 1.2 校验工作中的线程数大于等于容量,或者大于等于 coreSize or maxSize
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize)) // 如果core为true就判断是否大于coreSize,否则判断maxSize
return false;
// CAS修改 workerCount(+1)
if (compareAndIncrementWorkerCount(c))
// break 结束 retry 的 for 循环
break retry;
// 到这里可能是CAS失败了,重新获取 ctl
c = ctl.get();
// 如果线程池状态被更改
if (runStateOf(c) != rs)
continue retry; // 跳转到retry位置,重新判断
// else CAS failed due to workerCount change; retry inner loop
}
}
--------------------------------------------------------------------------------------------------------------
// 2.创建worker
// 2.1 创建标识变量
boolean workerStarted = false; // woker启动标识
boolean workerAdded = false; // woker成功加入worker容器标识
Worker w = null;
try {
// 2.2 构造worker。在worker的构造函数中会调用newThread方法创建一个Thread
// 注:由于Worker也实现了Runnable,所以在创建线程的时候是newThread(this)。这是一个巧妙的设计
w = new Worker(firstTask);
final Thread t = w.thread; // 获取worker中的线程
// 2.3 将worker加入到worker容器(Set)
if (t != null) {
final ReentrantLock mainLock = this.mainLock; // 这个mainLock是一个成员变量,作用是控制对worker的操作
// 加锁是因为,可能有多个线程同时要将worker放入worker容器
mainLock.lock();
try {
// 获取到线程池状态rs
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN || // 如果线程池状态是 RUNNING
(rs == SHUTDOWN && firstTask == null)) {
// 线程池是SHUTDOWN且要执行的任务为null
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
// 将当前woker加入到 HashSet<Worker> workers 中
workers.add(w);
int s = workers.size(); // 获取到 workers 的大小,即现在有几个worker
// 如果worker数已经大于了最大线程池容量
if (s > largestPoolSize)
largestPoolSize = s; // 将largestPoolSize设置为worker现在的书香
workerAdded = true; // 添加标志设置为成功
}
} finally {
mainLock.unlock(); // 解锁
}
--------------------------------------------------------------------------------------------------------------
// 3.启动如果woker中的线程。前提是worker已经添加成功
if (workerAdded) {
// 启动刚创建线程:Thread#start -> Worker#run -> runWorker
t.start();
workerStarted = true; // 线程启动标志置为true
}
}
} finally {
// 如果线程启动失败
if (! workerStarted)
addWorkerFailed(w);
}
// 返回线程是否启动成功
return workerStarted;
}
3.runWorker()
最初にタスクを取得してから、ワーカーにタスクを実行させます。このメソッドの一般的なロジックは次のとおりです。
- タスクを取得するには2つの方法があります
- firstTask:ワーカーの初期タスク
- getTask():タスクキューのタスク
- 実行中にスレッドがタスクにスローされないようにロックする
- スレッドプールが停止している場合は、現在のスレッドを中断します
- フック機能の前に実行
- タスクを実行します。つまり、task.run()を呼び出します。
- アフターフック機能を実行する
- 現在のタスクを削除し、ロックを解除します。次のタスクを実行している間
ここでもう1つ注意してください。目的は、現在のスレッドを維持してタスクを実行し続けることですが、スレッドがタスクの取得に失敗した場合(getTaskメソッドはブロックして待機します)、ループ、つまりスレッドを終了します。寿命が尽きたときにリサイクルされます。
final void runWorker(Worker w) {
Thread wt = Thread.currentThread(); // 获取当前线程
Runnable task = w.firstTask; // 尝试获取创建worker时的firstTask
w.firstTask = null; // 从这可以看出,只要firstTask执行过一次,就会一直被置为null
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
// 1.获取任务:如果firstTask已经被执行过了,就从任务队列中获取
// 注:通过while维持了线程的存活,并不断获取任务取执行。若迟迟拿不到任务,就会退出while结束线程
while (task != null || (task = getTask()) != null) {
// 2.锁住 worker,防止worker在执行任务时被丢入另一个任务
w.lock();
// 3.判断线程池若处于 stop 中,但线程没有到达中断状态,帮助线程中断
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
// 4.执行 before 钩子函数
beforeExecute(wt, task);
Throwable thrown = null;
try {
// 5.同步执行任务
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 {
// 6.执行 after 钩子函数
// 如果这里抛出异常,会覆盖 catch 的异常,所以这里异常最好不要抛出来
afterExecute(task, thrown);
}
} finally {
// 7.任务执行完成,删除任务,并计算解锁
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
// 做一些抛出异常的善后工作
processWorkerExit(w, completedAbruptly);
}
}
4.getTask()
ブロッキングキューからタスクを取得します。ブロックして待機した後にタスクが取得されない場合、タスクはnullを返します。そのため、現在のスレッドはrunWorkerメソッドのwhileループを終了し、リサイクルされます。これは、実行することが残っておらず、リソースが無駄になりました。具体的なリサイクル戦略はソースコードにあり、メソッドの一般的なプロセスは次のとおりです。
- 現在のスレッドをリサイクルする最初の判断:スレッドプールSHUTDOWN、およびキューは空です
- 2番目の判断は、現在のスレッドをリサイクルし、次の条件のいずれかを満たすことです。
- wc> maximumPoolSize && wc> 1:既存のワーカーの数がスレッドプールの最大容量を超えており、リサイクル後にスレッドプールに少なくとも1つのスレッドがあります
- wc> maximumPoolSize && workQueue.isEmpty():既存のワーカーの数がスレッドプールの最大容量を超えており、タスクキューが空です
- timed && timedOut && wc> 1:コアスレッドのリサイクルを許可するか、既存のスレッドの数がコアの数を超え、現在のスレッドがタイムアウトし、リサイクル後にスレッドプールに少なくとも1つのスレッドがあります
- timed && timedOut && workQueue.isEmpty():コアスレッドのリサイクルを許可するか、既存のスレッドの数がコアの数を超え、現在のスレッドがタイムアウトになり、タスクキューが空になります
- タスクキューからタスク(取得またはポーリング)を取得し、取得した場合は戻り、取得しなかった場合はタイムアウト(timedOut)をtrueに設定します。
注:timedがtrueの場合にのみ、ポーリングが使用されて待機します。 KeepAliveTimeの場合、それ以外の場合は、常に時間がかかります。それを待ちます
PS:ここでも、コアスレッドと非コアスレッドは概念的にのみ異なります。コード内のすべての人は同じであり、すべて通常のスレッドです。
private Runnable getTask() {
// 标识是否超时
// 默认false,但如果下面自旋中 poll 在 keepAliveTime(线程存活时间) 没等到任务,就会将timedOut置为true
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
int rs = runStateOf(c); // 获取线程池状态
// 1.第一次判断是否回当前收线程
// 线程池关闭 && 队列为空,不需要在运行了,直接返回null
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount(); // workerCount--
return null;
}
int wc = workerCountOf(c); // 获取worker个数
// timed的作用是决定在阻塞队列中等任务时用 poll 还是 take
// timed = 核心线程可以被灭亡(默认false) || 运行的线程数大于 coreSize
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
// 2. 第二次判断是否回收当前线程。组合后分为4种情况
if ((wc > maximumPoolSize || (timed && timedOut)) // woker大于线程池最大数量 || (timed && 当前线程已经超时)
&& (wc > 1 || workQueue.isEmpty())) {
// woker大于1 || 任务队列为空
// 通过CAS使workerCount--
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
// 3.从阻塞队列中获取任务。timed 决定了是使用 poll 还是 take
// keepAliveTime 是线程最大空闲时间,是构造线程池的入参
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : // poll,超时了就返回
workQueue.take(); // take,任务队列中没任务会阻塞等待
// 如果在队列拿到了任务就返回
if (r != null)
return r;
// 没拿到就将超时timedOut设置为true,表示此时队列没有数据
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}