如何在Java 8桌面应用程序中替换或重新启动死锁的Swing EventDispatchThread / EventQueue

时间:2015-02-12 23:42:20

标签: java swing event-dispatch-thread awt-eventqueue eventqueue

前段时间我们在我们的应用程序中添加了一些代码来检测并尝试从Swing EDT死锁中恢复,因此用户至少可以保存他们的文件(最好没有死锁,但......) 。在Java 1.6中,这很容易。检测到EDT已被阻塞足够长的时间,然后从后台线程调用:

EventQueue newQ = new EventQueue();
Toolkit.getDefaultToolkit().getSystemEventQueue().push(newQ);

新的UI事件将在新的EventQueue / EDT上处理,用户可以保存他们的工作。

在Java 8中,这不起作用,因为EventQueue.push的实现已被更改为将(阻塞的)EventDispatchThread从旧队列复制到新队列。

3 个答案:

答案 0 :(得分:0)

当然,我总能做一些有点邪恶的事情:

private static void hackAroundJava8Protections(EventQueue newQ) {
    try {
        Field field = newQ.getClass().getDeclaredField("dispatchThread");
        field.setAccessible(true);
        field.set(newQ, null);
        Method method = newQ.getClass().getDeclaredMethod("initDispatchThread");
        method.setAccessible(true);
        method.invoke(newQ);
    } catch (Throwable e) {
        throw new RuntimeException(e);
    }
}

这将启动一个新的EventDispatchThread,允许使用应用程序UI。我能像保存用户一样保存数据。我不确定可能会有什么缺点。也许有一种不那么可怕的方法可以重新启动被阻止的EDT?

答案 1 :(得分:-1)

try {
    Field field = EventQueue.class.getDeclaredField("dispatchThread");
    field.setAccessible(true);
    Thread dispatchThread = (Thread) field.get(systemEventQueue);
    field.set(systemEventQueue, null);
    dispatchThread.stop();
} catch (Exception e) {
    e.printStackTrace();
}

它仍然不是很好,但是可以。

initDispatchThread无需手动调用,因为dispatchThread为null时,EventQueue会自动执行此操作。

如果旧线程没有停止并在以后解除阻塞,那么一切都会变得疯狂。我想那时我们有两个线程来处理Queue,而这里没有为线程安全而构建,因此全部崩溃了。

我仍在寻找更好的解决方案。

我的另一个想法是创建自己的EventQueue并使用EventQueue.push(newQueue)将其替换为原来的Ev​​entQueue,但是查看EventQueue的代码可以对其进行扩展,但不能以必要的方式进行修改。重写它对我来说也很麻烦-那里发生了很多复杂的事情,我不想惹恼。

答案 2 :(得分:-1)

// run this when program starts to identify and remember the initial awtEventThread
Thread awtEventThread;
// identify the original thread:
EventQueue.invokeLater(() -> awtEventThread = Thread.currentThread());

// run this when a reset is neccessary:
EventQueue systemEventQueue = Toolkit.getDefaultToolkit().getSystemEventQueue(); // the currently active Queue
EventQueue newEventQueue1 = new EventQueue(); // helper Queue to create a new AWT-Event-Threads
newEventQueue1.postEvent(new InvocationEvent(this, () -> {})); // init new AWT-Event-Thread - it happens automatically when an event is posted
EventQueue newEventQueue2 = new EventQueue(); // the new queue we want to use
systemEventQueue.push(newEventQueue2); // copy thread & events from systemEventQueue
newEventQueue1.push(newEventQueue2); // copy thread & (no) events from newEventQueue1 *HACK*
awtEventThread.stop(); // stop the old thread to prevent two threads processing the Queue - would get MESSY
EventQueue.invokeLater(() -> awtEventThread = Thread.currentThread()); // update our awtEventThread variable for the next time

此解决方案不是很好,但是可以。而且无需反射和setAccessible(true)

我使用push()方法的一个实现细节将新创建的线程从newEventQueue1复制到newEventQueue2,后者继承了原始systemEventQueue的所有内容。

启动新线程并设置队列后,将终止旧线程NEEEDS。如果不是这样,以防万一它解除阻塞,它将继续处理队列,然后变得混乱。该系统尚未准备好由两个线程并行处理。