<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Java桌面技術

    Java Desktop Technology

    常用鏈接

    統計

    友情連接

    最新評論

    《FilthyRichClients》讀書筆記(一)-SwingのEDT

        《FilthyRichClients》讀完了前幾個章節,現將我的體會結合工作以來從事Swing桌面開發的經驗,對本書的一些重要概念進行一次分析,對書中的一些遺漏與模糊的地方及時補充,同時使讀者消除長期以來“Swing性能低、界面丑陋”諸如此類的舊觀念。讀書筆記僅談談我對Swing的理解,難免會犯錯誤,還望廣大讀者指教。

        書中第二章-Swing渲染基本原理 中對Swing的線程做了系統地介紹。相比其他同類Swing教程,已經講得非常深入了。但是如果讀者之前對線程的掌握程度有限,尤其是編寫代碼比較隨意的coder們,動輒就大量編寫類似下面這樣的代碼:
    jButton1.addActionListener(new ActionListener(){
       public void actionPerformed(ActionEvent e) {
        // TODO
       }
      });
    這樣的代碼可能是netBeans這樣的工具生成的“杰作”。但是如果這個人再懶惰一點,可能會直接在TODO下面寫上長長一堆代碼,還伴隨著不可預知的I/O操作,很多人指責界面被僵住是Swing性能的問題。在新式的JDK中,Swing已經在性能方面改進了很多,完全可以這么說:與應用程序自身的業務計算相比,界面上的耗時可以忽略。但是如果上述惡習改不掉的話,Swing永遠“快”不起來,SWT也同樣如此,因為它們都是單線程圖形工具包。
        書上有這樣一段話:“EventQueue的派發機制由單獨的一個線程管理,這個線程稱為事件派發線程(EDT)”。和其他很多桌面API一樣,Swing將GUI請求放入一個事件隊列中執行。如果不明白什么是事件隊列、EDT,它們是如何運作的,那么首先必須澄清四個重要的概念:分別是同步與異步、串行與并行、生產者消費者模式、事件隊列。(不同領域串行與并行的含義可能是不同的)
        同步與異步:同步是程序在發起請求后開始處理事件并等待處理的結果或等待請求執行完畢,在此之前程序被block住直到請求完成。而異步是當前程序發起請求后立即返回,當前程序不會立即處理該事件并等待處理的結果,請求是在稍后的某一時間才被處理。
        串行與并行:所謂串行是指多個要處理請求順序執行,處理完一個再處理下一個;并行可以理解為并發,是同時處理多個請求(實際上我們只能理解為是這樣,特別是CPU數目少于線程數的機器而言,真正意義的并發是不存在的,各個線程只是斷斷續續地交替地執行)。下圖演示了串行與并行的機制。可以這么說,在引入多線程之前,對于同一進程或者程序而言執行的都是串行操作。

    串行:  

    并行:

        生產者/消費者模式:可以想象這樣一副場景,某車間的一條傳送帶,有一個或多個入口不斷產生待加工的貨物,這種不斷產生貨物的稱為生產者;傳送帶的末端是一個或多個工人在加工貨物,稱作消費者。有時由于傳送帶上沒有足夠的貨物使得某一工人暫時空閑,有時又由于部分貨物需加工的時間較長出現傳送帶上待加工的貨物堆積。
                                                            
    如果用Java實現一個簡單的生產者消費者模型,利用線程的等待/通知機制很容易實現。給出最基本的同步隊列的參考實現

    public class SyncQueue<T> {
     private List<T> queue;

     private final Object LOCK = new Object();

     public SyncQueue() {
      queue = new LinkedList<T>();
     }

     public T pop() throws InterruptedException {
      synchronized (LOCK) {
       while (queue.isEmpty()) {
        try {
         LOCK.wait();
        } catch (InterruptedException ex) {
         throw ex;
        }
       }
       T e = queue.remove(0);
       return e;
      }
     }

     public void push(T e) {
      synchronized (LOCK) {
       queue.add(e);
       LOCK.notifyAll();
      }
     }
    }
    在JDK 5中新出現了許多具有并發性的數據結構在java.util.concurrent包中,它們適合于特殊的場合,本帖不作解釋。

        事件隊列:在計算機數據結構中,隊列是一個特殊的數據結構。其一、它是線性的;其二、元素是先進先出的,也就是說進入隊列的元素必須從末端進入,先入隊的元素先得到執行,后入隊的元素等待前面的元素執行完畢出隊后才能執行,隊列的處理方式是執行完一個再執行下一個。隊列與線程安全是兩個不同的概念,如果要將隊列加上線程安全的特性,只需要仿照上述生產者/消費者加上線程的等待/通知即可。

    而Swing的事件隊列就類似(基本原理相似,但是Swing內部實現會做些優化)于上述的事件隊列,說它是單線程圖形工具包指的是僅有單一消費者,也就是常說的事件分發線程(EDT),一般來講,除非你的應用程序停止,否則EDT會永不間斷地徘徊在處理請求與等待請求之間。下圖是Swing事件隊列的實現機制:



    很顯然,如果在加工某一個貨物上花費很長的時間,那么后續的貨物只好等待。對于單一線程的事件隊列來說有兩個非常突出的特性:一、將同步操作轉為異步操作。二、將并行處理轉換為串行順序處理

    如果你能理解上述圖,那么你就應該意識到:EDT要處理所有GUI操作,它是職責分明且非常忙碌的。也就是說你要記住兩條原則:一、職責分明,任何GUI請求都應該在EDT中調用。二、需要處理的GUI請求非常多,包括窗口移動、組件自動重繪、刷新,它很忙,所以任何與GUI無關的處理不要由EDT來負責,尤其是I/O這種耗時的操作。
        書中還講到Swing不是一個“安全線程”的API,為什么要這樣設計,再回看上圖就會明白:Swing的線程安全不是靠自身組件的API來保障,雖然repaint方法是這樣,但是大多數Swing API是非線程安全的,也就是說不能在任意地方調用,它應該只在EDT中調用。Swing的線程安全靠事件隊列和EDT來保障。
        invokeLater和invokeAndWait:前文提到,Swing自身不是線程安全,對非EDT的并發調用需通過invokeLater(runnable)和invokeAndWait(runnable)使請求插入到隊列中等待EDT去執行。invokeLater(runnable)方法是異步的,它會立即返回,具體何時執行請求并不確定,所以命名invokeLater是稍后調用。invokeAndWait(runnable)方法是同步的,它被調用結束會立即block當前線程(調用invokeAndWait的那個線程)直到EDT處理完那個請求。invokeAndWait一般的應用是取得Swing組件的數據,例如取得JSlider組件的當前值:
    public class Task implements Runnable {
     private JSlider slider;
     private int value;
     public Task() {
      //slider = ...;
     }
     @Override
     public void run() {
      try {
       Thread.sleep(1000); // 有意停住1秒
      } catch (InterruptedException e) {
      }
      value = slider.getValue();
     }
     public int getValue() {
      return value;
     }
    }
    而外部非EDT線程可以這樣調用:
    Task task = new Task();
      try {
       EventQueue.invokeAndWait(task);
      } catch (InterruptedException e) {
      } catch (InvocationTargetException e) {
      }
      int value = task.getValue();
    當線程運行到EventQueue.invokeAndWait(task)時會立即被block至少1秒,待invokeAndWait返回時已經可以安全地取到值了。invokeAndWait被這樣命名也反映了使用的意圖:調用并等待結果。invokeAndWait有非常重要的一條準則是它不能在EDT中被調用,否則程序會拋出Error,請求也不會去執行。

    public static void invokeAndWait(Runnable runnable)
                 throws InterruptedException, InvocationTargetException {

            if (EventQueue.isDispatchThread()) {
                throw new Error("Cannot call invokeAndWait from the event dispatcher thread");
            }

     class AWTInvocationLock {} // 聲明這個類只是鎖的標志,沒有其他意義
            Object lock = new AWTInvocationLock();

            InvocationEvent event =
                new InvocationEvent(Toolkit.getDefaultToolkit(), runnable, lock,
        true);

            synchronized (lock) {
                Toolkit.getEventQueue().postEvent(event); //添加進事件隊列
                lock.wait(); // block當前線程
            }

            Throwable eventThrowable = event.getThrowable();
            if (eventThrowable != null) {
                throw new InvocationTargetException(eventThrowable);
            }
        }


    為什么要有這樣一條限制?結合前文不難得出-防止死鎖。如果invokeAndWait在EDT中調用,那么首先將請求壓進隊列,然后EDT便被block(因為它就是調用invokeAndWait的當前線程)等待請求結束通知它繼續運行,而實際上請求將永遠得不到執行,因為它在等待隊列的調度使EDT執行它,這就陷入一個僵局-EDT等待請求先執行,請求又等待EDT對隊列的調度。彼此等待對方釋放鎖是造成死鎖的四類條件之一。Swing有意地避免了這類情況的發生。

        書中也提到了同步的繪制請求,作為隊列,一條基本原則就是先進先出。那么paintImmediately到底是怎樣的呢?顯然這個調用請求不會稍后去執行,也就是說不會插入到隊列的末尾等到排在它前面的請求執行完再去執行它,而是“破壞”順序性原則優先去執行,前面提到,Swing的事件隊列相對基礎的同步隊列做了很多優化,那么這么說它是否被插入到隊列最前面呢,也就是0這個位置?貌似也不是,書上說“已經在EDT中調用的方法中間...”,那么就是比當前正在處理的繪制請求還要優先,因為它是當前繪制請求的一部分,所以當前繪制請求(EDT正在處理的那個請求)要等它處理完成后再繼續處理。(好好體會吧)
        SwingWorker:推薦一篇Blog,http://blog.sina.com.cn/s/blog_4b6047bc010007so.html,作者是原Sun中國工程研究院的陳維雷先生,他對Swing的造詣非淺,他的Blog中有3篇介紹這一主題的文章,詳盡程度要比該書詳細得多。

        最后,談一下理解EDT對設計模式的幫助。通過上述對事件隊列和EDT的分析,有這樣一種體會:事件隊列是一個非常好的處理并發設計模型,不僅Swing用它來處理后臺,Java的很多地方都在用,只不過對于處理服務器端的并發請求有多個處理線程在等候處理請求,也就是常說的線程池。而對于單用戶的桌面應用,單線程調用要比多現成API更簡單,“Swing后臺這樣做是為了保證事件的順序可預見性”,而且相對于服務器,客戶端桌面層的請求要少得多,所以單線程就足夠應對了。
    單一Thread化的訪問

    通過EDT,使得不具備線程安全的Swing函數庫避開了并發訪問的問題。如果你也有一個不具備thread安全性的函數庫并想在multithreaded環境下使用應該怎么辦?只要你是從單一的thread來訪問這個函數庫,程序就不會遭遇到任何數據同步的問題。

    posted on 2008-06-23 22:49 sun_java_studio@yahoo.com.cn(電玩) 閱讀(10874) 評論(7)  編輯  收藏 所屬分類: NetBeans

    評論

    # re: 《FilthyRichClients》讀書筆記(一)-SwingのEDT 2008-06-24 08:09 日月雨林@gmail.com

    很有啟發性的一篇文章! 謝謝!  回復  更多評論   

    # re: 《FilthyRichClients》讀書筆記(一)-SwingのEDT 2008-06-24 13:35 zht

    FilthyRichClients對于java2d學習 是本好書  回復  更多評論   

    # re: 《FilthyRichClients》讀書筆記(一)-SwingのEDT 2008-06-25 09:41 Matthew Chen

    好久沒看lz的blog了。
    講到paintImmediately和“已經在EDT中調用的方法中間...”,就好像edt嚴格的先進后出提供的一個小變通,可以超越之前請求但未繪制事件先執行。
    我想到swt繪制的一個問題,如隨拖拽行為而產生的多個界面上的重繪,我們要作處理使其同時發生而沒有滯后,方法是在其中一個的paintcontrol中加入對另一個的redraw調用,后來看來api,不知是否是update這個方法能夠解決的問題。  回復  更多評論   

    # re: 《FilthyRichClients》讀書筆記(一)-SwingのEDT[未登錄] 2008-07-06 13:42 William Chen

    Hi樓主,
    好久不聯系了。我最近為Eclipse做了一個Swing的界面設計工具插件,模仿netbeans的,我已經把它開源了,如果你感興趣的話,希望你能加入進來一起開發。你看看這兒我寫的介紹:
    http://www.javaeye.com/topic/208787
    -William Chen  回復  更多評論   

    # re: 《FilthyRichClients》讀書筆記(一)-SwingのEDT 2008-07-10 13:05 sun_java_studio@yahoo.com.cn(電玩)

    今天在Javaeye上發現了一個Blog。
    http://blog.palantirtech.com/category/swing/

    值得借鑒  回復  更多評論   

    # re: 《FilthyRichClients》讀書筆記(一)-SwingのEDT 2008-07-15 17:31 ALLENME

    真是牛人聚集的地方啊。
    敢問一下:Willim chen現在還寫blog么?  回復  更多評論   

    # re: 《FilthyRichClients》讀書筆記(一)-SwingのEDT[未登錄] 2008-12-12 11:45 Matthew Chen

    再閱,果然溫故知新,看透了不少。  回復  更多評論   

    TWaver中文社區
    主站蜘蛛池模板: 又长又大又粗又硬3p免费视频| 亚洲精品av无码喷奶水糖心| 国产精品永久免费| 亚洲伊人久久成综合人影院| 一级做a爰黑人又硬又粗免费看51社区国产精品视 | 久久久国产精品福利免费| 亚洲尤码不卡AV麻豆| 免费成人在线电影| 久久久久亚洲AV片无码下载蜜桃| 亚洲精品免费在线视频| 亚洲卡一卡2卡三卡4麻豆| 成人人观看的免费毛片| 一本色道久久88亚洲精品综合| 免费无码又爽又高潮视频 | 成人精品视频99在线观看免费| 在线日韩日本国产亚洲| 日韩免费的视频在线观看香蕉| 久久亚洲中文字幕精品有坂深雪| ww在线观视频免费观看| 亚洲色最新高清av网站| 免费一级毛片免费播放| 中文字幕av免费专区| 亚洲美女激情视频| 久久久久免费看黄A片APP| 美女黄网站人色视频免费| 亚洲综合av永久无码精品一区二区| 久久福利青草精品资源站免费| 亚洲精品午夜久久久伊人| 在线免费观看一区二区三区| 全部一级一级毛片免费看| 亚洲AV无码成人专区片在线观看 | 最近中文字幕mv手机免费高清| 无码色偷偷亚洲国内自拍| 久久精品国产亚洲av麻| 日韩免费a级毛片无码a∨| xxxx日本在线播放免费不卡| 亚洲色图古典武侠| 亚洲人妻av伦理| 999久久久免费精品国产| 日韩成人毛片高清视频免费看| 久久精品国产亚洲av麻豆小说 |