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 находится там, без разбора.