1

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

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

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

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

4

3 回答 3

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?

于 2015-02-12T23:42:19.597 回答
-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

This solution is not very beautiful, but it works. And it works without reflection and setAccessible(true).

I use one immplementation detail of the push() method to copy the newly created Thread from newEventQueue1 to newEventQueue2, which inherited everything from the original systemEventQueue.

After the new Thread is started and the Queue is set up the old thread NEEEDS to be terminated. If not so - in case it unblocks it will continue to process the queue and then it gets messy. The system is not ready to be processed by two threads in parallel.

于 2019-08-07T18:03:19.590 回答
-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不需要手动调用,因为 EventQueue 在dispatchThread为 null 时会自动执行此操作。

如果旧线程没有停止并且稍后解除阻塞,那么一切都会变得疯狂。我想我们有两个线程处理队列,这里没有为线程安全构建任何东西,因此它全部崩溃了。

我仍在寻找更好的解决方案来做到这一点。

我的另一个想法是创建自己的 EventQueue 并用它替换原来的EventQueue.push(newQueue),但是查看 EventQueue 的代码可以扩展它,但不能以必要的方式修改。重写它对我来说也有问题——那里发生了很多复杂的事情,我不想搞砸。

于 2019-08-07T17:35:10.960 回答