前阵子,我们添加了一些代码,我们的应用程序来检测并尝试从一个Swing EDT死锁状态中恢复,所以用户至少可以保存自己的文件(这将是最好不要有一个僵局,但是......)。 在Java 1.6,这是很容易。 检测到EDT已被阻止的时间足够量,然后从后台线程调用此:
EventQueue newQ = new EventQueue();
Toolkit.getDefaultToolkit().getSystemEventQueue().push(newQ);
新的UI事件将在一个新的EventQueue / EDT进行处理,用户可以保存他们的工作。
在Java 8,因为EventQueue.push的执行已更改为(阻塞)EventDispatchThread从旧的队列复制到一个新的,这并不工作。
当然,我总是可以做的东西有点邪恶:
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,允许使用应用程序的用户界面。 我能保存数据,就好像我是一个用户。 我不知道什么缺点可能有。 也许有重新启动阻塞EDT的那么可怕呢?
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
为空。
如果旧的线程没有停止,后来疏导,一切都疯了。 我想,那么我们有两个线程处理队列中,并没有在这里建立线程安全所以都发生故障。
我还在寻找一个更好的解决办法来做到这一点。
另一个想法我已经是创造我自己的EventQueue与它使用替代原来的EventQueue.push(newQueue)
但寻找到EventQueue中的代码,它可以延长,但在方式neccessary没有改变。 和重写它看起来也是有问题的,我 - 有很多复杂的东西发生的事情,我不想惹。
// 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)
。
我使用的一个immplementation详细push()
方法来从新创建的线程复制newEventQueue1
到newEventQueue2
,它继承了从原来的systemEventQueue
。
新的线程启动及队列设置了旧线后NEEEDS将被终止。 如果不是这样的 - 如果它放开,将继续处理队列中,那么它就会变得混乱。 系统未准备好由并联的两个线程处理。
文章来源: How to replace or restart a deadlocked Swing EventDispatchThread/EventQueue in Java 8 desktop application