I.はじめに
今日、私の同僚が、システムがリリースされるたびに古いノード サービスのスレッド プールはどうなるのか、キュー内のタスクは実行されるのか、という質問をしました。
ブロガーは、正常な終了は実行が完了するまで待ってから終了する必要があると常々信じていたため、chatGpt でチャットすることになり、紆余曲折がありました。
2. 分析
1、ThreadPoolExecutor的退出
ブロガーは次のように尋ねました。Java サービスが終了すると、スレッド プールのキュー内のタスクはどうなりますか?
chatGpt の回答: Java サービスが終了するとき、スレッド プール内の未実行タスク (キュー内のタスク) の処理方法は、スレッド プールの種類と構成によって異なります。標準 の場合ThreadPoolExecutor
、Java サービスが終了すると、スレッド プールはまだ実行されていないタスクを完了しようとします。キュー内のタスクが実行されるのを待ち、キューが空になるまで各タスクを 1 つずつ処理します。これは、スレッド プールがシャットダウンする前に、送信されたすべてのタスクが完了するまで待機することを意味します。
デフォルトでは、ThreadPoolExecutor
非デーモン スレッドが使用されるため、スレッド プール内のスレッドは Java サービスの終了をブロックしませんが、キューが空になるまでタスクの実行を続けます。Java サービスが終了したときにスレッド プール内のタスクをすぐに停止したい場合は、スレッド プールのスレッドをデーモン スレッドとして設定できます ( を設定することで)。これにより、メイン スレッドが終了すると、デーモン スレッドが中断されて停止しますsetDaemon(true)
。タスクの実行。
こうして見ると、ブロガーの考えと一致していますが、ブロガーは別の問題を考えました。通常、ThreadPoolExecutor
ホスティングは Spring に委ねられており、Spring を考慮する必要があります。
@Bean
public ThreadPoolExecutor threadPoolExecutor() {
// 创建自定义的 ThreadPoolExecutor
// ...
}
2. 春のホスティングThreadPoolExecutor的退出
ブロガーは次のように尋ねました: Spring Boot は、@bean によって定義された ThreadPoolExecutor がタスクの実行を完了するまで待機しませんか?
chatGpt の答えは次のとおりです。Spring Boot では、@Bean
定義されたThreadPoolExecutor
ものはデフォルトでタスクの実行を待ちません。@Bean
Spring Boot アプリケーションがシャットダウンすると、シャットダウン フックがトリガーされ、 Spring コンテナ内のすべてのオブジェクト ( によって定義されたスレッド プールを含む) が自動的にシャットダウンされます。
デフォルトでは、Spring Boot はスレッド プール メソッドを呼び出してshutdown()
スレッド プールを閉じますが、その中のすべてのタスクが実行されるまで待機しません。実行中のタスクは引き続き実行されますが、未実行のタスクは破棄されます。
これを行うと、キュー ヒープ内のデータがすべてなくなるわけではなく、公開後にトラフィックが発生しても、トラフィックは完全に廃止されます。しかし、本当にそうなのでしょうか?
3. shutdownNow と shutdown の違い
Spring ではThreadPoolExecutor
スレッド プールを閉じるために shutdown メソッドが使用されているので、ソース コードに戻って確認してみましょう。その結果、shutdownNow メソッドが存在します。これは明らかに暴力性と優雅さの間に違いをもたらします。
2 つの方法の違いを確認する
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(SHUTDOWN);
interruptIdleWorkers();
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
tryTerminate();
}
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;
}
大きな違いは次のとおりです。
1. shutdownNow はタスクを取り出して戻ります
2. shutdownNow は cas を通じて STOP 状態を設定し、shutdown は SHUTDOWN 状態を設定します。
明らかに違いはこの状態にあるので、この状態はキュー タスクに影響を与えますか? キュー タスクの取得方法を見てください。
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// Are workers subject to culling?
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
明らかに、重要なのは if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) であり、STOP の場合、タスクはワーカーに返されませんが、SHUTDOWN の場合、キューは空です。 , そのため、シャットダウン メソッドはキュー内のタスクを実行し続けることになり、chatGpt が言ったこととは異なる結論が得られます。
4、Beanのdestroyメソッド
しかし、まだ問題があり、オブジェクトの破棄メソッドは通常、オブジェクトのシャットダウンを指定します。
@Bean(name = "handoverExecutor", destroyMethod = "shutdown")
public ThreadPoolExecutor handoverExecutor() {
return new ThreadPoolExecutor(20, 30,
600, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100), new ThreadPoolExecutor.DiscardPolicy());
}
ただし、場合によっては指定されず、デフォルトが使用される場合があります。では、デフォルトとは何でしょうか?
String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;
public static final String INFER_METHOD = "(inferred)";
これはどのようなメソッドで推論されているのでしょうか? リフレクションメソッドが取られているところを辿るとcloseせずにshutdownメソッドが取られており、ThreadPoolExecutorにはcloseメソッドがないのでデフォルトでshutdownを使っていることが分かります。
String destroyMethodName = beanDefinition.getDestroyMethodName();
if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName) ||
(destroyMethodName == null && closeableInterface.isInstance(bean))) {
// Only perform destroy method inference or Closeable detection
// in case of the bean not explicitly implementing DisposableBean
if (!(bean instanceof DisposableBean)) {
try {
return bean.getClass().getMethod(CLOSE_METHOD_NAME).getName();
}
catch (NoSuchMethodException ex) {
try {
return bean.getClass().getMethod(SHUTDOWN_METHOD_NAME).getName();
}
catch (NoSuchMethodException ex2) {
// no candidate destroy method found
}
}
}
return null;
}
return (StringUtils.hasLength(destroyMethodName) ? destroyMethodName : null);
3. まとめ
chatGpt のナレッジ検索は、単一の側面においては確かに比較的詳細かつ正確であり、Baidu、Google、技術コミュニティから直接大量のスピーチをフィルタリングするよりもはるかに優れており、優れた AI 検索エンジンであると言えます。
ただし、chatGpt の弱点は解析ができないことです。さまざまな知識を組み合わせると、与えられた情報が間違っている可能性があります。学生は、検索エンジンを盲目的に信頼せず、より権威のある書籍やソースコードを読み、技術コミュニティとコミュニケーションをとる必要があります。