最近調試程序時發現,點擊某個界面時會出現卡死的情況,出現的頻率還是比較頻繁的。
再次出現卡死的情況后,利用jvisualvm查看線程的運行情況,dump操作之后發現線程間出現了死鎖:
Found one Java-level deadlock:
=============================
"Thread-122":
waiting to lock monitor 0x484052e4 (object 0x1af2bb08, a com.raisecom.ems.templet.client.panel.SnmpTablePanel),
which is held by "AWT-EventQueue-0"
"AWT-EventQueue-0":
waiting to lock monitor 0x4861c81c (object 0x180d5950, a java.awt.Component$AWTTreeLock),
which is held by "Thread-122“
再在線程堆棧中查看根源的線程及方法,找到如下的代碼:
public void onSelectionChanged(SelectionChangedEvent e) { Object source = e.getSource(); if (source instanceof AbstractMenuTreePanel) { ///單起個線程處理顯示 Thread thread = new Thread(){ public void run() { if(!"".equals(DemarcationConfigCenterView.this.m_ProVer)) refreshConfigPanel2(); else refreshConfigPanel(); } } } }; thread.start(); }
EDT以外的線程中更新界面都需要SwingUtilities.invokeLater,修改代碼:
public void onSelectionChanged(SelectionChangedEvent e) { Object source = e.getSource(); if (source instanceof AbstractMenuTreePanel) { ///單起個線程處理顯示 Thread thread = new Thread(){ public void run() { if(!"".equals(DemarcationConfigCenterView.this.m_ProVer)) SwingUtilities.invokeLater(new Runnable() { public void run() { refreshConfigPanel2(); } }); else{ SwingUtilities.invokeLater(new Runnable() { public void run() { refreshConfigPanel(); } }); } } }; thread.start(); }
測試沒有發生客戶端卡死的現象了。
當swing界面程序啟動的時候,會啟動3個進程, 1、主線程 2、系統工具包線程:負責捕獲操作系統事件,然后將事件轉換成swing的事件,然后發送到事件派發線程EDT 3、事件派發線程(EDT):將事件派發到各個組件,並負責調用繪制方法更新界面
所有的事件,例如鍵盤,鼠標事件,都會由工具包線程轉換成swing事件,然后放到事件隊列EventQueue中,而這個EventQueue的派發機制是由EDT來管理的。 所以任何修改組件狀態的方法都應該在EDT中執行,包括構造方法。Swing這樣的構造原理經常會造成的情況就是,在EDT中執行長時間的事件,使EDT不能及時響應更新界面的事件,就是所說的界面卡住,這種不光是新手就是比較熟練的程序員也會犯的一個錯誤。所以必須避免在EDT中執行長時間的操作,而避免的方法就是多線程,啟動另外的線程來處理冗長的操作,比如操作數據庫,讀寫文件等,在這過程中可能要更新界面來給用戶以提示,比如顯示一個進度條,過一段事件更新一下界面,但是在EDT以外的線程中更新界面都是無效的,這在前面已經說過,要更新界面就要將對界面的更新操作放到EDT中,但是事件又是在另外的線程中執行的,要解決這個問題就要使用SwingUtilities提供的一個方法了 invokeLater,
public void actionPerformed(ActionEvent e){ new Thread(new Runnable(){ //do something SwingUtilities.invokeLater(new Runnable(){ pulic void run(){ //update the GUI } }); }).start; }
這個方法的作用就是將一個更新界面的任務放到EDT中,EDT會在適當的時候進行調用以更新界面。invokeLater負責創建一個含有Runnable的特定事件,並讓其在EDT中排隊等待調用,當被調用時就會運行Runnable中的run方法進行派發。
http://www.cnblogs.com/lnlvinso/p/3685863.html