スレッド プールを使用する場合、shutdown() メソッドを呼び出した後、スレッド プールは新しい実行タスクを受け入れなくなります。ただし、shutdown() メソッドを呼び出す前にタスク キューに配置されたタスクは、引き続き実行する必要があります。このメソッドはノンブロッキング メソッドであり、呼び出されるとすぐに戻り、タスク キュー内のすべてのタスクが実行されるのを待ってから戻ります。以下に示すように、shutdown() メソッドのソース コードを見てみましょう。
public void shutdown() { //获取线程池的全局锁 final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { //检查是否有关闭线程池的权限 checkShutdownAccess(); //将当前线程池的状态设置为SHUTDOWN advanceRunState(SHUTDOWN); //中断Worker线程 interruptIdleWorkers(); //为ScheduledThreadPoolExecutor调用钩子函数 onShutdown(); // hook for } finally { //释放线程池的全局锁 mainLock.unlock(); } //尝试将状态变为TERMINATED tryTerminate(); }
一般に、shutdown() メソッドのコードは比較的単純で、最初にスレッド プールを閉じる権限があるかどうかを確認し、権限がある場合は、中断する権限があるかどうかを再度確認します。権限がない場合は SecurityException がスローされ、コードは次のようになります。
//检查是否有关闭线程池的权限 checkShutdownAccess(); //将当前线程池的状态设置为SHUTDOWN advanceRunState(SHUTDOWN); //中断Worker线程 interruptIdleWorkers();
このうち、checkShutdownAccess()メソッドの実装コードは以下のとおりです。
private void checkShutdownAccess() { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkPermission(shutdownPerm); final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { for (Worker w : workers) security.checkAccess(w.thread); } finally { mainLock.unlock(); } } }
checkShutdownAccess() メソッドのコードを理解するのは比較的簡単です。このメソッドは、スレッド プールのグローバル ロックが使用されている間、スレッド プールを閉じる権限があるかどうかを検出します。
次に、次に示すように、advancedRunState(int) メソッドのソース コードを確認します。
private void advanceRunState(int targetState) { for (;;) { int c = ctl.get(); if (runStateAtLeast(c, targetState) || ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c)))) break; } }
advanceRunState(int) メソッドの全体的なロジックは、現在のスレッド プールの状態が指定された状態であるかどうかを判断することです。shutdown() メソッドで渡される状態は SHUTDOWN です。SHUTDOWN の場合は、直接返される; if SHUTDOWN でない場合は、現在のスレッド プールのステータスを SHUTDOWN に設定します。
次に、以下に示すように、showdown() メソッドによって呼び出されるinterruptIdleWorkers() メソッドを見てみましょう。
private void interruptIdleWorkers() { interruptIdleWorkers(false); }
interruptIdleWorkers() メソッドがinterruptIdleWorkers(boolean) メソッドを呼び出していることがわかります。次に示すように、interruptIdleWorkers(boolean) メソッドのソース コードを続けて見てください。
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(); } }
上記のコードの全体的なロジックは次のとおりです。スレッド プールのグローバル ロックを取得し、すべてのワーカー スレッドを循環し、スレッドが中断されているかどうかを検出し、中断されていない場合はワーカー スレッドがロックを取得します。スレッドの割り込みメソッドを実行し、スレッドが取得したロックを解放します。このとき、onlyOne パラメータが true の場合、ループを終了します。それ以外の場合は、すべてのワーカー スレッドをループして同じ操作を実行します。最後に、スレッド プールのグローバル ロックが解放されます。
次に、shutdownNow() メソッドを見てみましょう。
スレッド プールの shutdownNow() メソッドが呼び出されると、スレッド プールは新しい実行タスクを受け入れなくなり、タスク キューに存在するタスクも破棄されます。実行中のワーカー スレッドもすぐに中断されます。同時に、メソッドはすぐに戻ります。このメソッドの戻り値は、現在のタスク キュー内の破棄されたタスクのリストです。
shutdownNow() メソッドのソース コードは次のとおりです。
public List<Runnable> shutdownNow() { List<Runnable> tasks; final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { //检查是否有关闭权限 checkShutdownAccess(); //设置线程池的状态为STOP advanceRunState(STOP); //中断所有的Worker线程 interruptWorkers(); //将任务队列中的任务移动到tasks集合中 tasks = drainQueue(); } finally { mainLock.unlock(); } /尝试将状态变为TERMINATED tryTerminate(); //返回tasks集合 return tasks; }
shutdownNow() メソッドのソース コードの全体的なロジックは、shutdown() メソッドと基本的に同じですが、shutdownNow() メソッドがスレッド プールのステータスを STOP に設定し、すべてのスレッド プールを中断する点が異なります。ワーカー スレッドを作成し、タスク キューに入れます。すべてのタスクがタスク コレクションに移動され、返されます。
shutdownNow() メソッドがすべてのスレッドに割り込むと、interruptWorkers() メソッドが呼び出されることがわかります。次に、以下に示すinterruptWorkers() メソッドのソース コードを見てみましょう。
private void interruptWorkers() { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { for (Worker w : workers) w.interruptIfStarted(); } finally { mainLock.unlock(); } }
interruptWorkers() メソッドのロジックは比較的単純です。スレッド プールのグローバル ロックを取得し、すべてのワーカー スレッドを循環し、スレッドを順番に割り込み、最後にグローバル ロックを解放します。スレッドプール。
スレッドを中断するために、interruptWorkers() メソッド内で、Worker クラスのinterruptIfStarted() メソッドが実際に呼び出されます。以下に示すように、Worker クラスのinterruptIfStarted() メソッドのソース コードを見てみましょう。 。
void interruptIfStarted() { Thread t; if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) { try { t.interrupt(); } catch (SecurityException ignore) { } } }
本質的には、スレッドを中断するために Thread クラスの中断() メソッドを呼び出していることがわかりました。
スレッド プールが awaitTermination(long, TimeUnit) メソッドを呼び出すと、スレッド プールのステータスが TERMINATED に変更されるまで、呼び出し元のスレッドはブロックされます。 . リターンするか、タイムアウト期間に達するとリターンします。次に、以下に示す awaitTermination(long, TimeUnit) メソッドのソース コードを見てみましょう。
public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { //获取距离超时时间剩余的时长 long nanos = unit.toNanos(timeout); //获取Worker线程的的全局锁 final ReentrantLock mainLock = this.mainLock; //加锁 mainLock.lock(); try { for (;;) { //当前线程池状态为TERMINATED状态,会返回true if (runStateAtLeast(ctl.get(), TERMINATED)) return true; //达到超时时间,已超时,则返回false if (nanos <= 0) return false; //重置距离超时时间的剩余时长 nanos = termination.awaitNanos(nanos); } } finally { //释放锁 mainLock.unlock(); } }
上記のコードの全体的なロジックは次のとおりです: まずワーカー スレッドの排他ロックを取得し、次にループして現在のスレッド プールが既に TERMINATED 状態かどうかを確認します。そうであれば、直接 true を返します。それ以外の場合は、タイムアウトしたかどうかを確認し、タイムアウトした場合は false を返します。タイムアウトしない場合は、タイムアウトまでの残り時間をリセットしてください。次に、次のサイクルに入り、現在のスレッド プールが TERMINATED 状態かどうかを再度確認します。そうであれば、直接 true を返します。それ以外の場合は、タイムアウトしたかどうかを確認し、タイムアウトした場合は、false を返します。タイムアウトしない場合は、タイムアウトまでの残り時間をリセットしてください。このループは、スレッド プールのステータスが TERMINATED になるかタイムアウトになるまで継続します。
以上がJava スレッド プールを正常にシャットダウンするにはどうすればよいですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。