Приложение занимает очень много времени, чтобы прекратить

Мы написали консольное приложение (будет использоваться как сервис), которое запускает несколько рабочих потоков для обработки запросов, поступающих через mina. Приложение выходит из основного цикла при получении сигнала остановки на конкретном сетевом порту. Это предполагаемый способ прекращения обслуживания. Это работает вполне нормально, но при получении сигнала остановки процесс приложения не завершается немедленно (занимает до 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)

Операционная система Ubuntu 14.04 LTS.

Я не имею ни малейшего понятия об этом поведении, и я надеюсь получить некоторые подсказки о том, как исследовать эту проблему дальше.

Дополнительная информация

Я произвел полный дамп потока как предложено. Четыре темы ждут:

"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) добился цели...

1 ответ

Решение

Я предполагаю, что вы создали 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); Это не идеально, поскольку потоки могли бы выполнять полезную работу, если они были сделаны недемонами по уважительной причине, но если поток запускается сторонней библиотекой, это может быть вашим самым простым вариантом.


Что-то, что вы можете попробовать, - это проследить, где созданы все потоки. Одним из способов является использование отладчика для останова конструктора ThreadPoolExecutor или любой другой ключевой метод, который покажет вам, где создается пул.

Или вы можете использовать профилировщик памяти с трассировкой распределения. Затем посмотрите, где создан каждый объект Thread.

Другой вариант - изменить класс Thread, включив в него трассировку стека того, где он был создан (или запущен). Это можно сделать, создав собственную версию и предварительно ожидая ее в пути к загрузочному классу.

Другие вопросы по тегам