前段时间我们在我们的应用程序中添加了一些代码来检测并尝试从Swing EDT死锁中恢复,因此用户至少可以保存他们的文件(最好没有死锁,但......) 。在Java 1.6中,这很容易。检测到EDT已被阻塞足够长的时间,然后从后台线程调用:
EventQueue newQ = new EventQueue();
Toolkit.getDefaultToolkit().getSystemEventQueue().push(newQ);
新的UI事件将在新的EventQueue / EDT上处理,用户可以保存他们的工作。
在Java 8中,这不起作用,因为EventQueue.push的实现已被更改为将(阻塞的)EventDispatchThread从旧队列复制到新队列。
答案 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)
将其替换为原来的EventQueue,但是查看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。如果不是这样,以防万一它解除阻塞,它将继续处理队列,然后变得混乱。该系统尚未准备好由两个线程并行处理。