Java "Thread-2" без стека предотвращает прерывание
У меня довольно сложная Java-программа, которая не заканчивается.
Отладчик eclipse показывает поток, который можно приостановить, но не имеет трассировки стека.
Он называется "Thread-2".
Выход jstack -l
для этого потока:
"Thread-2" #17 prio=5 os_prio=0 tid=0x00007f1268002800 nid=0x3342 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
Я добавил точку останова в Thread.start(), но я не могу найти поток, называемый "Thread-2".
Поток появляется только после создания двух потоков "AWT-Event-Queue".
Я не создаю ни одного потока вручную в своей программе.
После того, как основной поток и все остальные потоки будут удалены, а JFrame будет удален, следующие потоки все еще существуют:
Thread [AWT-EventQueue-0] (Running)
Thread [Thread-2] (Running)
Thread [DestroyJavaVM] (Running)
При приостановке виртуальной машины существуют следующие потоки:
Daemon System Thread [Signal Dispatcher] (Suspended)
Daemon System Thread [Finalizer] (Suspended)
Daemon System Thread [Reference Handler] (Suspended)
Daemon System Thread [Java2D Disposer] (Suspended)
Daemon System Thread [AWT-XAWT] (Suspended)
Thread [AWT-EventQueue-0] (Suspended)
Thread [Thread-2] (Suspended)
Thread [DestroyJavaVM] (Suspended)
Как получить дополнительную информацию об этом потоке или разрешить его завершение?
РЕДАКТИРОВАТЬ 1:
В соответствии с Dependency Hierarchy
представления eclipse pom.xml
я использую следующие сторонние библиотеки:
guava 17.0 [compile]
hamcrest-core 1.3 [test]
junit 4.11 [test]
log4j-api 2.0-beta9 [compile]
log4j-core 2.0-beta9 [compile]
ИЗМЕНИТЬ 2:
Добавление точек останова ко всем конструкторам класса потоков, как предлагается в qaru.site/info/501595/..., я вижу, что Thread-0
и Thread-1
создаются log4j
, но не Thread-2
. Он по-прежнему остается прежним, и при его построении не срабатывает точка останова.
ИЗМЕНИТЬ 3:
Теперь он становится жутким. Даже метод stop()
работает при вызове в потоке. Я добавил его в код, указанный в qaru.site/info/501595/....
По крайней мере, System.exit(int)
все еще работает. Но, как сказано в комментарии, я не хочу использовать это.
РЕДАКТИРОВАТЬ 4:
Информация о моей системе:
- Я запускаю новейшую стабильную версию Ubuntu 15.10 Wily. У меня есть репозитории
security
, updates
и backports
.
- Моя версия JVM:
java version "1.7.0_91"
OpenJDK Runtime Environment (IcedTea 2.6.3) (7u91-2.6.3-0ubuntu0.15.10.1)
OpenJDK 64-Bit Server VM (build 24.91-b01, mixed mode)
РЕДАКТИРОВАТЬ 5:
Я выполнил программу с Java-версией jre-8u71-linux-x64
, непосредственно загружаемой с java.com, но ошибка сохраняется. jstack -l
показывает ту же странную нить. Обратите внимание, что программа по-прежнему была построена с более старой версией Java. Edit: После компиляции с java8u72 с oracle.com, я получаю такое же поведение.
РЕДАКТИРОВАТЬ 6:
Я повторил все поля нитей, вот вывод. Я не могу получить какой-либо намек на эти поля, нить даже не имеет цели.
name: [[email protected]
priority: 5
threadQ: null
eetop: 140274638530560
single_step: false
daemon: false
stillborn: false
target: null
group: java.lang.ThreadGroup[name=main,maxpri=10]
contextClassLoader: null
inheritedAccessControlContext: [email protected]
threadInitNumber: 3
threadLocals: null
inheritableThreadLocals: null
stackSize: 0
nativeParkEventPointer: 0
tid: 17
threadSeqNumber: 20
threadStatus: 5
parkBlocker: null
blocker: null
blockerLock: [email protected]
MIN_PRIORITY: 1
NORM_PRIORITY: 5
MAX_PRIORITY: 10
EMPTY_STACK_TRACE: [Ljava.lang.StackTraceElement;@453da22c
SUBCLASS_IMPLEMENTATION_PERMISSION: ("java.lang.RuntimePermission" "enableContextClassLoaderOverride")
uncaughtExceptionHandler: null
defaultUncaughtExceptionHandler: null
threadLocalRandomSeed: 0
threadLocalRandomProbe: 0
threadLocalRandomSecondarySeed: 0
EDIT 7:
Добавлена точка наблюдения в поле name
Thread
. Он доступен только моему аналитическому коду и, кажется, никогда не написан...
EDIT 8:
jstack
-F -m выдает ошибку для моей программы:
Attaching to process ID 10973, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.71-b15
Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at sun.tools.jstack.JStack.runJStackTool(JStack.java:140)
at sun.tools.jstack.JStack.main(JStack.java:106)
Caused by: java.lang.RuntimeException: Unable to deduce type of thread from address 0x00007ff68000c000 (expected type JavaThread, CompilerThread, ServiceThread, JvmtiAgentThread, or SurrogateLockerThread)
at sun.jvm.hotspot.runtime.Threads.createJavaThreadWrapper(Threads.java:169)
at sun.jvm.hotspot.runtime.Threads.first(Threads.java:153)
at sun.jvm.hotspot.tools.PStack.initJFrameCache(PStack.java:200)
at sun.jvm.hotspot.tools.PStack.run(PStack.java:71)
at sun.jvm.hotspot.tools.PStack.run(PStack.java:58)
at sun.jvm.hotspot.tools.PStack.run(PStack.java:53)
at sun.jvm.hotspot.tools.JStack.run(JStack.java:66)
at sun.jvm.hotspot.tools.Tool.startInternal(Tool.java:260)
at sun.jvm.hotspot.tools.Tool.start(Tool.java:223)
at sun.jvm.hotspot.tools.Tool.execute(Tool.java:118)
at sun.jvm.hotspot.tools.JStack.main(JStack.java:92)
... 6 more
Caused by: sun.jvm.hotspot.types.WrongTypeException: No suitable match for type of address 0x00007ff68000c000
at sun.jvm.hotspot.runtime.InstanceConstructor.newWrongTypeException(InstanceConstructor.java:62)
at sun.jvm.hotspot.runtime.VirtualConstructor.instantiateWrapperFor(VirtualConstructor.java:80)
at sun.jvm.hotspot.runtime.Threads.createJavaThreadWrapper(Threads.java:165)
... 16 more
Имя класса странного потока: java.lang.Thread
.
Я не использую аргументы командной строки для выполнения программы. Добавление опции -Dlog4j2.disable.jmx=true
дает странному потоку имя Thread-1
.
Я обновил log4j
до 2.5, а странный поток теперь имеет имя Thread-0
, когда задана опция -Dlog4j2.disable.jmx=true
и Thread-1
, если это не так.
EDIT 9:
Полностью удаленный log4j, ошибка сохраняется. Теперь поток называется Thread-0
.
Здесь проект, если это помогает.
Ответы
Ответ 1
Я не понимаю, почему точка останова на Thread.start()
не работала, но вы также можете попробовать перехватить поток → creation < < установив точку останова на конструкторы Thread или на метод (внутренний) Thread.init()
.
Тот факт, что поток имеет имя Thread-2
, подразумевает, что он был создан одним из конструкторов, который генерирует имя потока по умолчанию. Это говорит о том, что он не был создан JVM или стандартными библиотеками классов Java. Он также сужает конструкторы, которые могли быть использованы для его создания.
Как получить дополнительную информацию об этой теме...
Я не могу думать ни о чем другом, кроме установки контрольных точек.
... или разрешить его завершение?
Если вы можете найти, где он создан, вы можете использовать setDaemon(true)
, чтобы пометить его как поток демона. Однако это нужно сделать до начала потока.
Другая возможность - найти поток, пройдя дерево ThreadGroup
и затем называть Thread.interrupt()
на нем. (Thread.getAllStackTraces()
- еще один способ отслеживания объекта потока.) Однако нет никакой гарантии, что поток будет "уважать" прерывание и завершить работу.
Наконец, вы можете просто вызвать System.exit(...)
.
UPDATE
Я упомянул, что поток может не уважать interrupt()
, и я не удивлен, что stop()
не работает. (Он устарел и даже не может быть реализован на некоторых платформах.)
Однако, если вам удалось реализовать код, который на самом деле находит тайну потока, вы можете копаться, чтобы найти либо подкласс Thread
, либо Runnable
, с которым он был создан. Если вы можете распечатать полное имя класса, это даст вам общее представление о том, откуда оно взялось. (Предполагая, что вы все еще не добились успеха в контрольных точках, вам может понадобиться использовать "неприятное" отражение, чтобы извлечь runnable из частного потока target
).
Ответ 2
Не уверен, достаточно ли этого для вас, но следующий код позволит вам попробовать interrupt
any Thread
по его имени:
//Set of current Threads
Set<Thread> setOfThread = Thread.getAllStackTraces().keySet();
//Iterate over set to find yours
for(Thread thread : setOfThread){
if (thread.getName().equals("Thread-2")) {
thread.interrupt();
break;
}
}
Кроме того, посмотрите на статью от JavaSpecialists, которая пытается идентифицировать создателя Thread на основе того факта, что конструктор Thread
делает вызов менеджеру безопасности. Если мы добавим пользовательскую SecurityManager
в нашу Систему, мы можем отслеживать инициатор Thread.
Ответ 3
Прежде всего, вы должны изменить свой флаг _exit
на volatile
, так как он читает из одного потока (ваш основной метод) и записывается другим (обработчик событий JCF/Swing), поэтому возможно, что ваш основной поток isn "получить" свежую "ценность. В частности: поток может сохранять поле в регистре CPU, а не перезагружать его из памяти по мере цикла." volatile" предотвратит это поведение:
private volatile boolean _exit;
Однако, исходя из ваших стековых следов, я не думаю, что ваша проблема, так как мы не видим вашего основного метода там. Но это должно быть сделано в любом случае, это просто хорошая практика.
Предполагая, что это не исправить, я предполагаю, что ваша проблема в том, что у вас есть хотя бы одно другое окно (кроме AgentFrame
), которое не удаляется. Нить AWT не остановится, пока не будут удалены все окна.
Поместите это в конец вашего основного метода:
System.out.println(Arrays.toString(Window.getWindows()))
Я предполагаю, что вы увидите больше, чем просто ваш DrawFrame
. Если бы я догадался, я бы сказал, что UISettingsFrame
находится там, без разбора.