基本的なスレッドプールのインタビュー分析

スレッドプールのクラス図

エグゼキュータ==> ExcutorService ==> AbstractExecutorService ==> ThreadPoolExecutor分析します。

 

  • URL継承階層の上には、インターフェースのトップレベルは、スレッドプールエグゼキュータで、このインタフェースは、唯一の方法ボイドは(Runnableをコマンド)を実行しています
  • エグゼキュータ継承ExecutorServiceのは、いくつかの主要な新しい方法(Runnableを(コーラブル))、シャットダウン、shutdownNowの提出など
  • AbstractExecutorServiceの実装方法上記いくつかのインタフェースExecutorServiceの。
  • ThreadPoolExecutor継承AbstractExecutorService、主要な方法のいくつかを達成するために、スレッドプール(Runnableを)を実行します。

 

AbstractExecutorService

次のようにAbstractExecutorServiceは、方法書を提出達成しました:

(呼び出し可能タスク)提出方法

  1. パブリック<T>将来<T>提出する(呼び出し可能<T>タスク){
  2. (タスク== nullは))(新しいNullPointerExceptionが投げた場合。
  3. RunnableFuture <T> ftask = newTaskFor(タスク)。
  4. (ftask)を実行します。
  5. ftask返します。
  6. }

newTaskFor(呼び出し可能呼び出し可能)方法

  1. 保護<T> RunnableFuture <T> newTaskFor(呼び出し可能<T>呼び出し可能){
  2. <T>(呼び出し可能)新しいFutureTaskを返します。
  3. }

FutureTask上記RunnableFutureインタフェースを実現し、RunnableFutureはRunnableをと今後インターフェースを継承します。ボイドランのみ、Runnableインタフェース方法、今後のインターフェイスは(boolean)を取り消し、V得る()、V得る(長いタイムアウト、TimeUnitでユニット)、ブールisCancelled()、ブールisDone()メソッド。

ThreadPoolExecutor

次に実行するAbstractExecutorService.submitメソッド呼び出し(ftask)上に、これはThreadPoolExecutorを実行することです。その後、我々は、開始点として、メソッドを実行するために分析することがあります。

実行

  1. 公共ボイドは、{(Runnableをコマンド)を実行します
  2. (コマンド== null)の場合
  3. 新しいNullPointerExceptionがスロー();
  4. INT C = ctl.get()。
  5. IF(workerCountOf(C)<corePoolSize){
  6. もし(addWorker(コマンド、真))
  7. リターン;
  8. C = ctl.get()。
  9. }
  10. IF(isRunning(C)&& workQueue.offer(コマンド)){
  11. INT再検査= ctl.get()。
  12. (もし!isRunning(再検査)&&削除(コマンド))
  13. (コマンド)を拒絶します。
  14. それ以外の場合(workerCountOf(再検査)== 0)
  15. addWorker(ヌル、偽);
  16. }
  17. それ以外の場合(!addWorker(コマンド、偽))
  18. (コマンド)を拒絶します。
  19. }
  • まず、チェックワーカースレッドの現在の数が少なくcorePoolSizeよりも、このタスク(commadn)を処理するために労働者を追加し、10未満の場合には、成功したが返されたタスクを追加しています。
  • スレッドが実行中の状態のままであり、非動作している場合、正常キュー、スレッドプールの状態を再確認し、スレッドプールに追加タスクは、キューからジョブを削除した場合、呼び出しの成功は拒否し、ポリシーに従って行うためにここに拒否し、現在の場合ワーカースレッドの数が0である場合、作業者を追加(ここで注意することは、addWorker(NULL、偽)、最初のパラメータと上記addWorkerと同じではありません)
  • あなたが追加した場合、作業者はまた、実行方法を拒否することができません。

 

addWorker

 

  1. プライベートブールaddWorker(RunnableをfirstTask、ブールコア){
  2. リトライ:
  3. にとって (;;) {
  4. INT C = ctl.get()。
  5. INT RS = runStateOf(C);
  6.  
  7. //必要に応じて、キューが空にのみかどうかを確認してください。
  8. もし(RS> = SHUTDOWN &&
  9. (RS == SHUTDOWN &&
  10. firstTask == nullの&&
  11. workQueue.isEmpty()))
  12. falseを返します。
  13.  
  14. にとって (;;) {
  15. INT WC = workerCountOf(C);
  16. (WC> = CAPACITYもし||
  17. WC> =(コアcorePoolSize:?maximumPoolSize))
  18. falseを返します。
  19. もし(compareAndIncrementWorkerCount(c)参照)
  20. 再試行を破ります。
  21. C = ctl.get()。//再読み込みCTL
  22. もし(runStateOf(C)!= RS)
  23. 再試行を続けます。
  24. //他のCASはworkerCount変化により失敗しました。内側のループを再試行してください
  25. }
  26. }
  27.  
  28. ブールworkerStarted =偽;
  29. ブールworkerAdded =偽;
  30. = NULL Wワーカー。
  31. {試します
  32. W =新しいワーカー(firstTask)。
  33. 最後のスレッドT = w.thread。
  34. もし(T!= NULL){
  35. 最終ReentrantLockのmainLock = this.mainLock。
  36. mainLock.lock();
  37. {試します
  38. //再確認ロックを保持しながら。
  39. ThreadFactory故障の場合、または//バックアウト
  40. //ロックを取得する前にシャットダウンします。
  41. INT RS = runStateOf(ctl.get())。
  42.  
  43. (RS <SHUTDOWNもし||
  44. (RS == SHUTDOWN && firstTask == NULL)){
  45. もし(t.isAlive())//事前チェックtが始動可能であること
  46. 新しいないIllegalThreadStateExceptionを投げます();
  47. (W)workers.add。
  48. INT S = workers.size()。
  49. もし(S> largestPoolSize)
  50. largestPoolSize = S;
  51. =真workerAdded。
  52. }
  53. } 最後に {
  54. mainLock.unlock();
  55. }
  56. {(workerAdded)場合
  57. t.start();
  58. =真workerStarted。
  59. }
  60. }
  61. } 最後に {
  62. (もし!workerStarted)
  63. (W)addWorkerFailed。
  64. }
  65. workerStartedを返します。
  66. }

(1)意思決定ロジックが複雑で、我々は最初に見

  1. もし(RS> = SHUTDOWN &&
  2. (RS == SHUTDOWN &&
  3. firstTask == nullの&&
  4. workQueue.isEmpty()))
  5. falseを返します。

決意の条件は明らかにトゥーレ、直接returnfalseであれば、現在の状態は、SHUTDOWNよりも大きい場合。判定条件が偽の場合SHUTDOWN未満の現在の状態は、その後、ダウンした場合場合(スレッドプールRUNNING状態は、よく理解)(まあ理解し、スレッドプールは、新しいワーカーAを追加していない確かにクローズされる)と等しいの現在の状態SHUTDOWN:firstTaskキュー仕事がnullに等しいとタスクを持っている場合、それはダウンを実行していきますが、条件がfalseの場合、コードが戻らないと判断され、nullに等しくされていない、またはfirstTask作業キューが空の場合、条件が真であると判断され、falseを返します(最初のヌルと等しい場合イェジンハオは、そのため。我々は、スレッドプールは、もはや新しいタスクを受け入れているSHUTDOWN状態を知っていませんが、タスクは作業キューやジョブを完了するためにされている、このことを理解し、作業キューは、タスクを持っていますが、またし続けます起工。反対の場合は、それがダウンして行くことはありません)(2)ワーカースレッドの現在の数を決定するために

  1. にとって (;;) {
  2. INT WC = workerCountOf(C);
  3. (WC> = CAPACITYもし||
  4. WC> =(コアcorePoolSize:?maximumPoolSize))
  5. falseを返します。
  6. もし(compareAndIncrementWorkerCount(c)参照)
  7. 再試行を破ります。
  8. C = ctl.get()。//再読み込みCTL
  9. もし(runStateOf(C)!= RS)
  10. 再試行を続けます。
  11. //他のCASはworkerCount変化により失敗しました。内側のループを再試行してください
  12. }

ワーカースレッドの現在の数は制限パラメータは、スレッドプールの設定を超えていない、労働者を追加するにはCASの使用は、ループのための外側のうち、ダウンを実行し続けます。それ以外の場合はfalseを返し、労働者が失敗した追加。(3)上記工程12の完了後、実行する新しいワーカー(firstTask)、tはw.threadスレッドプールを=スレッドと再び状態をチェックが、有効であれば、現在の作業スレッドプールワーカーHashSetのに加え、実行T .start。タスクを実行するために、サブスレッドを開く前に、この時間。

サブスレッドのrunメソッド

通話t.start上記のステップ3は、実行方法は、労働者で実行するサブスレッドを開きます。

  1. ます。public void実行(){
  2. runWorker(この);
  3. }
  4.  
  5. 最終的な空隙runWorker(Wワーカー){
  6. スレッド重量=にThread.currentThread()。
  7. 実行可能なタスクはw.firstTaskを=。
  8. w.firstTask = NULL;
  9. w.unlock(); //割り込みを許可します
  10. ブールcompletedAbruptly =はtrue。
  11. {試します
  12. 一方、(タスク!= NULL ||(タスク= getTask())!= NULL){
  13. w.lock();
  14. //プールは、スレッドが中断されたことを確認、停止されている場合は、
  15. //そうでない場合は、確実にスレッドが中断されていません。この
  16. //に対処するために、第2のケースで再チェックが必要です
  17. // shutdownNowのレース割り込みをクリアしながら、
  18. もし((runStateAtLeast(ctl.get()、STOP)||
  19. (Thread.interrupted()&&
  20. runStateAtLeast(ctl.get()、STOP)))&&
  21. !wt.isInterrupted())
  22. wt.interrupt();
  23. {試します
  24. beforeExecute(WT、タスク)。
  25. Throwableをスロー= NULL;
  26. {試します
  27. task.run();
  28. }最後に {
  29. afterExecute(タスク、スロー)。
  30. }
  31. } 最後に {
  32. タスク= NULL;
  33. w.completedTasks ++;
  34. w.unlock();
  35. }
  36. }
  37. completedAbruptly = falseは、
  38. } 最後に {
  39. processWorkerExit(completedAbruptly、W)。
  40. }
  41. }

連続getTask()メソッド、ワークキューからの取得タスクを通じて労働者の上に、何のタスクが取得されていない場合、processWorkerExitメソッドが呼び出されます。

getTask()

  1. プライベートRunnableをgetTask(){
  2. ブールTIMEDOUT =偽; //最後のポーリング()タイムアウトしましたか?
  3. にとって (;;) {
  4. INT C = ctl.get()。
  5. INT RS = runStateOf(C);
  6. //必要に応じて、キューが空にのみかどうかを確認してください。
  7. IF(RS> = SHUTDOWN &&(RS> = STOP || workQueue.isEmpty())){
  8. decrementWorkerCount();
  9. ヌルを返します。
  10. }
  11. INT WC = workerCountOf(C);
  12. //カリングへの労働者の対象はいますか?
  13. ブールの時限= allowCoreThreadTimeOut || トイレ> corePoolSize。
  14. もし((WC> maximumPoolSize ||(時限式&& TIMEDOUT))
  15. &&(WC> 1 || workQueue.isEmpty())){
  16. もし(compareAndDecrementWorkerCount(c)参照)
  17. ヌルを返します。
  18. 継続する;
  19. }
  20. {試します
  21. RunnableをR =タイミング?
  22. workQueue.poll(keepAliveTimeが、TimeUnit.NANOSECONDS):
  23. workQueue.take();
  24. もし(R!= NULL)
  25. Rを返します。
  26. TIMEDOUTは真=。
  27. }キャッチ(InterruptedExceptionあるリトライ){
  28. TIMEDOUT = falseは、
  29. }
  30. }
  31. }

getTaskが無限ループ法のための方法であり、それは最初のスレッドプールの現在の状態を決定します

  1. IF(RS> = SHUTDOWN &&(RS> = STOP || workQueue.isEmpty())){
  2. decrementWorkerCount();
  3. ヌルを返します。
  4. }

この判断も当RS == SHUTDOWNは、ワークキューが空の場合、明らかにそれは直接はnullを返す必要があり、作業の前に1つのワーカーによって低減され、理解されています。(getTaskリターンヌルは、runWorker方法はHashSetのワーカーからprocessWorkerExit削除電流を呼び出し); RSは> SHUTDOWNより大きい(スレッドプールshutdownNowのこの方法に対応する、ワークキューは、タスクが実行されていない待機中の)場合、そうでない場合、スレッドプールに記載しましたランニング、ダウンを実行し続けます。次に、スレッドの最大数は、スレッドプールは、現在設定され、時間およびステータス行coreThreadワークキュー上で許可するかどうかは、リターンヌル保存一緒にスレッドのCAS番号で動作するかどうかを判定する。最後に、我々は作業キュー3ヘッド事業から次の作業に注意を取得したいです。

  1. RunnableをR =タイミング?
  2. workQueue.poll(keepAliveTimeが、TimeUnit.NANOSECONDS):
  3. workQueue.take();

トゥーレ(trueにallowCoreThreadTimeOutセット)にタイミングを合わせて、より多くの待機時間よりもまだワークキューR =ヌルからタスクを達成していない、でも、この時点で、それは以下workerCount corePoolSizeよりも起こす可能性がある場合は、現在の作業員も回収することができます。時限式はfalseが、メソッドをコールブロッキングは、ワークキューからタスクを取得する場合は、newFixedThreadPoolは常にワークキューが空の場合でも、閉じられたスレッドプールを表示せずに達成するように、ブロックするメソッドを呼び出しますが、また、ワーカースレッドの固定数を維持するために、 。

shutDown(shutDownNow)方法

  1. 公共の一覧<Runnableを> shutdownNowの(){
  2. 一覧<Runnableを>タスク。
  3. 最終ReentrantLockのmainLock = this.mainLock。
  4. mainLock.lock();
  5. {試します
  6. checkShutdownAccess();
  7. SHUTDOWNとして// shutDwonNow STOPとして、シャットダウン
  8. advanceRunState(STOP);(advanceRunState(SHUTDOWN);)
  9. interruptWorkers();(interruptIdleWorkers)
  10. // shutdownNowの特別な
  11. タスク= drainQueue();
  12. //シャットダウン特別ScheduledThreadPoolExecutorのコールバック
  13. onShutdown();
  14. } 最後に {
  15. mainLock.unlock();
  16. }
  17. tryTerminate();
  18. タスクを返します。
  19. }

シャットダウン及びshutDownnNow差法(コードレベル):

  • shutdownNowの:advanceRunState(STOP)、interruptWorkersシャットダウン:advanceRunState(シャットダウン)、interruptIdleWorkers
  • シャットダウンよりonShutdown();のScheduledThreadPoolExecutor onShutDown複製方法。
  • shutdownNowの方法は、キュー未完了のタスクを動作します。
  • interruptIdleWorkers

interruptIdleWorkers与interruptWorkers

(1)shutdownNowの

  1. プライベート無効interruptWorkers(){
  2. 最終ReentrantLockのmainLock = this.mainLock。
  3. mainLock.lock();
  4. {試します
  5. (:労働者ワット労働者)のために
  6. w.interruptIfStarted();
  7. } 最後に {
  8. mainLock.unlock();
  9. }
  10. }

明らかに、これは中断され、すべてのスレッド(2)シャットダウンされます

  1. プライベート無効interruptIdleWorkers(ブール天然記念物){
  2. 最終ReentrantLockのmainLock = this.mainLock。
  3. mainLock.lock();
  4. {試します
  5. {(作業者Wワーカー)のために
  6. トン= w.threadスレッド。
  7. (もし!t.isInterrupted()&& w.tryLock()){
  8. {試します
  9. t.interrupt();
  10. }キャッチ(SecurityExceptionが無視){
  11. } 最後に {
  12. w.unlock();
  13. }
  14. }
  15. もし(天然記念物)
  16. ブレーク;
  17. }
  18. } 最後に {
  19. mainLock.unlock();
  20. }
  21. }

パラメータを天然記念物に注意してください、これが唯一の内部コールtryTerminate()メソッド呼び出しのinterruptIdleWorkers(真)に、それ以外の場合は、その停止方法のために、interruptIdleWorkers(偽)であるが、すべてが中断スレッドされていない破るしようとしています。この方法は、次に(2)上記3)tryTerminate tryTerminate方法になります

  1. 最終的な空隙tryTerminate(){
  2. にとって (;;) {
  3. INT C = ctl.get()。
  4. もし(isRunning(C)||
  5. runStateAtLeast(C、片付け)||
  6. (runStateOf(C)== SHUTDOWN &&!workQueue.isEmpty()))
  7. リターン;
  8. 終了する資格(workerCountOf(C)!= 0){//もし
  9. interruptIdleWorkers(ONLY_ONE)。
  10. リターン;
  11. }
  12.  
  13. 最終ReentrantLockのmainLock = this.mainLock。
  14. mainLock.lock();
  15. {試します
  16. IF(ctl.compareAndSet(C、ctlOf(片付け、0))){
  17. {試します
  18. )(終了。
  19. } 最後に {
  20. ctl.set(ctlOf(TERMINATED、0));
  21. termination.signalAll();
  22. }
  23. リターン;
  24. }
  25. } 最後に {
  26. mainLock.unlock();
  27. }
  28. 失敗したCASに//他の再試行
  29. }
  30. }

上記のコードから分かるようにワーカースレッドの数がスレッドプールの状態がシャットダウンされた場合、空の、ワークキュースレッドプールは0又はSTOP状態、ワーカースレッド0の数であり、スレッドプールは、最終的TERMINATED状態にあり、すべてのウェイクアップしますtermination.awaitNanos(nanos値)のコールawaitTermination()メソッドブロックはスレッドを覚ますしていないため。

  1. パブリックブールawaitTermination(長いタイムアウト、TimeUnitでユニット)
  2. InterruptedExceptionが{スロー
  3. 長いnanos値= unit.toNanos(タイムアウト)。
  4. 最終ReentrantLockのmainLock = this.mainLock。
  5. mainLock.lock();
  6. {試します
  7. にとって (;;) {
  8. もし(runStateAtLeast(ctl.get()、TERMINATED))
  9. trueを返します。
  10. IF(nanos値<= 0)
  11. falseを返します。
  12. nanos値= termination.awaitNanos(nanos値)。
  13. }
  14. } 最後に {
  15. mainLock.unlock();
  16. }
  17. }

方法上記TryTerminate、addWorkerFailed()、processWorkerExit()、シャットダウン()、shutdownNowの()、削除(Runnableをタスク)メソッドが呼び出さにします。

スレッドプールを説明するための5つの状態

スレッドプールの上記頻繁に実行されている状態、ここでは簡単な説明。

  1. プライベート静的最終int型のRUNNING = -1 << COUNT_BITS。
  2. プライベート静的最終int型のSHUTDOWN = 0 << COUNT_BITS。
  3. プライベート静的最終int型のSTOP = 1 << COUNT_BITS。
  4. プライベート静的最終int型の片付け= 2 << COUNT_BITS。
  5. = 3 << COUNT_BITS TERMINATEDプライベート静的最終int型。

状態の定義

  • RUNNING:新しいタスクを、ワークキュー内のタスクを処理します。
  • SHUTDOWN:新しいタスクを受け入れませんが、タスクを完了していきますワークキュー
  • STOP:すべての実行中のタスクを打破しようと、新しいタスクがワークキュー未完のタスクを扱いません同意しません
  • 片付け:すべてのタスクが完了した、ワーカースレッドの数は、スレッドプールの状態が片付けが終わる()メソッドを呼び出しますなり、0です。
  • TERMINATED:終了()メソッドが完了しました

5つの状態変換

  • RUNNING - > SHUTDOWN:コールシャットダウン()メソッドは、おそらく暗黙のファイナライズ中()メソッド
  • (RUNNINGまたはSHUTDOWN) - > STOP:コールshutdownNowの()メソッド
  • SHUTDOWN - >片付け:ワークキューとプールが空であります
  • STOP - >片付け:プールが空であります
  • 片付け - > TERMINATED:終了()方法完成

出典:http://yeming.me/2016/05/07/threadPool1/

公開された277元の記事 ウォン称賛65 ビュー380 000 +

おすすめ

転載: blog.csdn.net/ailiandeziwei/article/details/104749570