[英]How to replace or restart a deadlocked Swing EventDispatchThread/EventQueue in Java 8 desktop application
不久前,我們在應用程序中添加了一些代碼,以檢測並嘗試從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,從而允許使用應用程序UI。 我能夠像保存用戶一樣保存數據。 我不確定可能會有什么弊端。 也許有一種不太可怕的方法來重新啟動被阻止的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
不需要手動調用,因為當dispatchThread
為null時,EventQueue會自動執行此操作。
如果舊線程沒有停止並在以后取消阻塞,那么一切都會變得瘋狂。 我想那時我們有兩個線程處理Queue,並且這里沒有構建線程安全的東西,因此一切都崩潰了。
我仍在尋找更好的解決方案來做到這一點。
我的另一個想法是創建自己的EventQueue並使用EventQueue.push(newQueue)
其替換為原來的EventQueue.push(newQueue)
,但是查看EventQueue的代碼可以對其進行擴展,但不能以必要的方式進行修改。 重寫它對我來說也很麻煩-那里發生了很多復雜的事情,我不想惹惱。
// 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()
方法的一個實現細節,將新創建的Thread從newEventQueue1
復制到newEventQueue2
,后者從原始systemEventQueue
繼承了所有內容。
在啟動新線程並設置隊列后,將終止舊線程NEEEDS。 如果不是這樣,以防萬一它解除阻塞,它將繼續處理隊列,然后變得混亂。 系統尚未准備好由兩個線程並行處理。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.