簡體   English   中英

如何在Java 8桌面應用程序中替換或重新啟動死鎖的Swing EventDispatchThread / EventQueue

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM