8

min 経由で受信するリクエストを処理するための複数のワーカー スレッドを開始するコンソール アプリケーション (サービスとして使用される) を作成しました。特定のネットワーク ポートで停止信号が受信されると、アプリケーションはメイン ループを離れます。これは、サービスを停止する意図された方法です。これは問題なく動作しますが、停止信号を受信して​​も、アプリケーションのプロセスはすぐには終了しません (最大 5 分かかります)。メイン関数が予想どおりにすぐに終了し、アプリケーションによって作成されたすべてのスレッドも終了することをログ メッセージで確認しました。しかし、アプリケーションは実行され続けます。

メイン関数を終了する前にまだ実行されているスレッドは次のとおりです。

Signal Dispatcher (java.lang.Thread)
Finalizer (java.lang.ref.Finalizer$FinalizerThread)
Abandoned connection cleanup thread (com.mysql.jdbc.AbandonedConnectionCleanupThread)
main (java.lang.Thread)
pool-2-thread-1 (java.lang.Thread)
Reference Handler (java.lang.ref.Reference$ReferenceHandler)

現在、次の Java バージョンを使用しています。

java version "1.7.0_80"
Java(TM) SE Runtime Environment (build 1.7.0_80-b15)
Java HotSpot(TM) 64-Bit Server VM (build 24.80-b11, mixed mode)

OSはubuntu 14.04 LTSです。

私はこの動作について手がかりがありません。その問題をさらに調査する方法についてのヒントを期待しています。

追加情報

提案どおり、完全なスレッド ダンプを作成しました。4 つのスレッドが待機しています。

"pool-2-thread-1" prio=10 tid=0x00007fd7fc51f000 nid=0x16200 waiting on condition [0x00007fd800318000]
   java.lang.Thread.State: TIMED_WAITING (parking)
    at sun.misc.Unsafe.park(Native Method)
    - parking to wait for  <0x00000000cceaf660> (a java.util.concurrent.SynchronousQueue$TransferStack)
    at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:226)
    at java.util.concurrent.SynchronousQueue$TransferStack.awaitFulfill(SynchronousQueue.java:460)
    at java.util.concurrent.SynchronousQueue$TransferStack.transfer(SynchronousQueue.java:359)
    at java.util.concurrent.SynchronousQueue.poll(SynchronousQueue.java:942)
    at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1068)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:745)

"Abandoned connection cleanup thread" daemon prio=10 tid=0x00007fd7fc23d800 nid=0x161e2 in Object.wait() [0x00007fd800cbb000]
   java.lang.Thread.State: TIMED_WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x00000000dc2af720> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
    - locked <0x00000000dc2af720> (a java.lang.ref.ReferenceQueue$Lock)
    at com.mysql.jdbc.AbandonedConnectionCleanupThread.run(AbandonedConnectionCleanupThread.java:41)

"Finalizer" daemon prio=10 tid=0x00007fd7fc066000 nid=0x161d6 in Object.wait() [0x00007fd801bd6000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x00000000dc03c060> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
    - locked <0x00000000dc03c060> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151)
    at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)

"Reference Handler" daemon prio=10 tid=0x00007fd7fc064000 nid=0x161d5 in Object.wait() [0x00007fd801cd7000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x00000000dc03c108> (a java.lang.ref.Reference$Lock)
    at java.lang.Object.wait(Object.java:503)
    at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133)
    - locked <0x00000000dc03c108> (a java.lang.ref.Reference$Lock)

pool-2-thread-1 について調査する必要があることを誰かが確認できますか? この結果をどう解釈してよいかわかりません。

最終的に、 acceptor.dispose (MINA) への呼び出しがうまくいきました...

4

1 に答える 1

6

非デーモンとして実行されている ExecutorService を作成したと思います。これは、このプールを使用しない場合shutdown()、スレッドが正常に停止するまでアプリケーションを実行し続けることを意味します (数分間使用されたため)。

デーモン スレッドで ExecutorService を作成して、アプリケーションがこのスレッド プールの停止を待つ必要がないことを明確にすることができます。(または、明示的にシャットダウンすることもできます)

String poolName = ....
exec = Executors.newCachedThreadPool(r -> {
    Thread t = new Thread(r, poolName);
    t.setDaemon(true);
    return t;
});

pool-2-thread-1 は意図的に作成したものではありません

これはライブラリで作成できますが、ExecutorService の形式に従います。注:pool-2..アプリケーションが作成する最初のプールではないことを示しています。

を使用して非システム スレッドを強制的に終了させることができますSystem.exit(0); 。スレッドが正当な理由で非デーモンにされた場合、スレッドが有用な作業を行っている可能性があるため、これは理想的ではありませんが、スレッドがサード パーティのライブラリによって開始された場合は、最も簡単なオプション。


あなたが試すことができるのは、すべてのスレッドが作成された場所をトレースすることです。1 つの方法は、デバッガーを使用して、ThreadPoolExecutorまたはプールが作成されている場所を示すその他の主要なメソッドのコンストラクターをブレークポイントにすることです。

または、メモリ プロファイラーを割り当てのトレースと共に使用することもできます。次に、すべての Thread オブジェクトが作成される場所を調べます。

もう 1 つのオプションは、Thread クラスを変更して、それが作成された (または開始された) 場所のスタック トレースを含めることです。これを行うには、独自のバージョンをビルドし、それをブート クラス パスに追加します。

于 2015-05-08T09:43:47.357 に答える