2010-02-11 71 views
7

我正在開發一個小應用程序,它將具有Swing GUI。應用程序正在另一個線程中執行IO任務,當該線程完成時,GUI應該根據線程的操作結果進行更新。在(工作者,非GUI)中運行的類在構造器中傳遞給它的對象將用於更新GUI,所以我不需要將GUI的東西放在非GUI類中,而是傳遞用於更新GUI的對象到那個班。 正如我所瞭解的表格閱讀(線程/擺動)更新(更改)安全選項Swing GUI將使用javax.swing.SwingUtilities.invokeLater()javax.swing.SwingUtilities.invokeLaterWait()和/或javax.swing.SwingWorker()基本上是做同樣的事情。使用invokeLater或SwingWorker從另一個線程更新Swing組件

這與Swing所有線程問題對我來說是有點混亂,但我需要使用線程做任何事情在GUI應用程序有意義,而不是掛GUI處理時,在美國東部時間,所以自己感興趣的事情,現在是這樣的:

  1. invokeLaterinvokeLaterWait如發送消息,美國東部時間並等待它做它時,它完成將原本調用之前處理消息?

  2. 它與Swing線程安全性方面是正確的,做這樣的事情:

    interface IUPDATEGUI { 
        public void update(); 
    } 
    
    // in EDT/where I can access components directly  
    class UpdateJList implements IUPDATEGUI {  
        public void update() {  
        // update JList...  
        someJList.revalidate();  
        someJList.repain();  
        }  
    }  
    
    class FileOperations implements Runnable { 
        private IUPDATEGUI upObj; 
        List<File> result = new ArrayList<File>; // upObject is accessing this 
    
        public void FileOperations(IUPDATEGUI upObj) { 
        this.upObj = upObj; 
        } 
    
        private void someIOTask() { 
        // ... 
        // IO processing finished, result is in "result" 
        } 
    
        public void run() { 
        someIOTask(); 
        javax.swing.SwingUtilities.invokeLater(new Runnable() { 
         public void run() { 
         upObj.update(); // access result and update JList 
         } 
        };); 
        } 
    } 
    

如果這是不正確的那麼應該如何進行?如果可能的話,我寧願使用invokeLater而不是SwingWorker,因爲我不需要改變我的整個班級,它在某種程度上更加整潔/獨特(我喜歡在Win32應用程序中發送消息)。

在此先感謝。

回答

0

invokeLater很好。這將調用放入AWT事件隊列中,以便在適當的時候在EDT中執行。你的程序將繼續運行,並且不會等待你的可調用函數被調用。

+0

在這種情況下更新組件後是否需要調用revalidate()和repaint()? – Mil 2010-02-12 00:33:55

5

使用invokeLater()invokeAndWait()將Runnable參數傳遞到隊列中等待在EDT中執行。因此,在EDT能夠處理請求時,調用invokeLater()將導致Runnable在EDT中執行。 invokeAndWait()只是等待(在調用線程中),直到執行發生。

如果您想執行後臺任務,在執行結束或中間狀態時通知EDT,使用SwingWorker是理想選擇。一個例子是將進程的當前進度傳遞給JProgressBar。

對於你的例子,似乎SwingWorker是一個更好的選擇,但如果你不想改變你的代碼太多,那麼在過程完成時調用invokeLater()就會很好。

+1

您能告訴我更多爲什麼您認爲SwingerWorker在我的情況下會是更好的選擇? – Mil 2010-02-12 00:34:59

+0

我想我明白了。如果我錯了,請糾正我,基本上我可以在SwingWorker類的兩種方法中更新GUI組件,例如,我可以在doInBackground()中更新JProgressBar,並在結束時更新JList, SwingWorker類的方法? – Mil 2010-02-12 02:16:24

+2

當您想在進程仍在運行時向GUI發佈中間更新時,可以使用調用EDT中的process()方法的swingworker的publish()方法。當EDT中的過程完成時,調用done()方法。所以你有兩個選擇來更新GUI。一個進程正在運行(發佈)和完成(完成)。閱讀它:http://java.sun.com/javase/6/docs/api/javax/swing/SwingWorker.html 這真的很有幫助。 – 2010-02-12 11:41:57

3

我建議在java 7之前不要使用invokeAndWait。我發現一個虛假的喚醒方法可能會導致非常痛苦的錯誤。對我來說,它導致了一些非常罕見而且很難調試的空指針異常。

http://bugs.sun.com/view_bug.do?bug_id=6852111

它固定爲7的java的B77。

+0

+1發佈關於虛假喚醒......「等待一個循環」是條件的標準成語,所以我想知道他們爲什麼沒有在這裏開始。 – 2010-02-11 22:38:22

+0

我在多年前發佈了代碼以「修復」。實際上應該不存在問題,因爲Sun JVM從不虛擬地喚醒。如果您看到NPE,可能是其他原因。 /無論如何,'invokeAndWait'往往會導致死鎖,所以應該避免。 – 2010-02-11 23:20:27

+0

看到這對老CR:http://bugs.sun.com/view_bug.do?bug_id=4974934 http://bugs.sun.com/view_bug.do?bug_id=4932181 – 2010-02-12 15:19:30