Swing中耗時任務需要另起新線程,這個新線程中更新GUI的操作仍需由EDT來做(轉)


最近調試程序時發現,點擊某個界面時會出現卡死的情況,出現的頻率還是比較頻繁的。

再次出現卡死的情況后,利用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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM