SwingUtilities的invokeLater和invokeAndWait

事件派發(fā)線程(EDT)

理解SwingUtilities類作用的前提是先理解事件派發(fā)線程的概念捞镰。
當(dāng)運(yùn)行一個(gè) Swing 程序時(shí),會(huì)自動(dòng)創(chuàng)建三個(gè)線程。
1.主線程岸售,負(fù)責(zé)執(zhí)行main 方法几迄。

  1. toolkit 線程,負(fù)責(zé)捕捉系統(tǒng)事件冰评,比如鍵盤(pán)、鼠標(biāo)移動(dòng)等木羹,程序員不會(huì)有任何代碼在這個(gè)線程上執(zhí)行甲雅。Toolkit線程的作用是把自己捕獲的事件傳遞給第三個(gè)線程,也就是事件派發(fā)線程坑填。
  2. 事件派發(fā)線程(EDT抛人,Event Dispatcher Thread),顧名思義是用來(lái)派發(fā)事件(根據(jù)事件找到對(duì)應(yīng)的事件處理代碼)的線程脐瑰。EDT接收來(lái)自 toolkit 線程的事件妖枚,并且將這些事件組織成一個(gè)隊(duì)列,EDT的工作內(nèi)容就是將這個(gè)隊(duì)列中的事件按照順序派發(fā)給相應(yīng)的事件監(jiān)聽(tīng)器苍在,并且調(diào)用事件監(jiān)聽(tīng)器中的回調(diào)函數(shù)绝页,這也意味著,所有的事件處理代碼都是在EDT而不是主線程中執(zhí)行寂恬。
    上面說(shuō)到EDT中維護(hù)了一個(gè)事件的隊(duì)列续誉,并且它們是按照順序派發(fā)的。由于事件派發(fā)是單線程的操作初肉,所以只有等待前面事件監(jiān)聽(tīng)器的回調(diào)函數(shù)執(zhí)行完畢酷鸦,才能夠執(zhí)行組件更新的操作,以及繼續(xù)派發(fā)后面的事件牙咏。這樣導(dǎo)致的一個(gè)后果就是:當(dāng)在一個(gè)事件監(jiān)聽(tīng)回調(diào)函數(shù)中做了耗時(shí)的操作臼隔,那么,界面會(huì)因此停住妄壶,并且界面上所有控件失效(不可觸發(fā))摔握。
    解決這個(gè)問(wèn)題的方法是:在事件處理函數(shù)中將耗時(shí)的操作放到新線程(一般稱之為工作線程)中執(zhí)行,而不是讓其在EDT中執(zhí)行盯拱。比如下面的例子盒发。

案例

一個(gè)窗口,有一個(gè)按鈕和一個(gè)label狡逢。點(diǎn)擊按鈕宁舰,系統(tǒng)將做模仿導(dǎo)入數(shù)據(jù)的動(dòng)作,導(dǎo)入數(shù)據(jù)之前需要檢測(cè)數(shù)據(jù)的合法性奢浑。并且蛮艰,檢測(cè)數(shù)據(jù)和導(dǎo)入數(shù)據(jù)這兩個(gè)步驟都需要耗費(fèi)一定的時(shí)間。
如果沒(méi)有之前說(shuō)到的EDT的概念雀彼,那么你可能會(huì)這么做:

importBtn.addActionListener(new ActionListener() {
           @Override
            public void actionPerformed(ActionEvent e) {
                try{
                   lb.setText("1.檢查數(shù)據(jù)合法性...");
                   Thread.sleep(3000);//模仿檢測(cè)數(shù)據(jù)合法性
                   lb.setText("2.正在導(dǎo)入數(shù)據(jù)...");
                   Thread.sleep(4000);//模仿導(dǎo)入數(shù)據(jù)
                   lb.setText("3.導(dǎo)入成功!");
                }catch (InterruptedException e1) {
                   e1.printStackTrace();
                }
            }
        });

但是壤蚜,如果運(yùn)行一下的話即寡,會(huì)發(fā)現(xiàn)現(xiàn)象是這樣:點(diǎn)擊按鈕,界面卡住袜刷,按鈕變得不可觸發(fā)聪富,直到一段時(shí)間(7秒)之后界面顯示“3.導(dǎo)入成功”。期間并沒(méi)有顯示“1.檢查數(shù)據(jù)合法性”和“2.正在導(dǎo)入數(shù)據(jù)”著蟹。
這個(gè)現(xiàn)象印證了上面說(shuō)的理論:當(dāng)事件派發(fā)線程中正在執(zhí)行的事件監(jiān)聽(tīng)函數(shù)執(zhí)行完畢墩蔓,才能進(jìn)行UI組件的刷新操作,并且派發(fā)事件隊(duì)列中的下一個(gè)萧豆。

下面是修改后的代碼奸披,將耗時(shí)的操作放在一個(gè)新的工作線程中執(zhí)行:

importBtn.addActionListener(newActionListener() {
            @Override
            public voidactionPerformed(ActionEvent e) {
                new Thread(new Runnable() {//開(kāi)辟一個(gè)工作線程
                    @Override
                    public void run() {
                        try {
                            lb.setText("1.檢查數(shù)據(jù)合法性...");
                           Thread.sleep(3000);//模仿檢測(cè)數(shù)據(jù)合法性
                            lb.setText("2.正在導(dǎo)入數(shù)據(jù)...");
                           Thread.sleep(4000);//模仿導(dǎo)入數(shù)據(jù)
                            lb.setText("3.導(dǎo)入成功!");
                        } catch(InterruptedException e1) {
                           e1.printStackTrace();
                        }
                    }
                }).start();
            }
        });

主題

下面到了SwingUtilities的內(nèi)容。在swing編程中有一個(gè)編程原則:所有的界面相關(guān)的更新涮雷,都應(yīng)該在 EDT 上執(zhí)行阵面,否則會(huì)導(dǎo)致界面繪制出現(xiàn)不穩(wěn)定性錯(cuò)誤。這也就意味著上面代碼的lb.setText("2.正在導(dǎo)入數(shù)據(jù)...");應(yīng)該在EDT中洪鸭,而非新的工作線程中執(zhí)行样刷。這樣就出現(xiàn)了一個(gè)矛盾:耗時(shí)的操作必須要在工作線程中執(zhí)行,否則會(huì)出現(xiàn)界面刷新不及時(shí)和卡頓的現(xiàn)象览爵,而工作線程中的界面刷新代碼又會(huì)導(dǎo)致界面繪制的不穩(wěn)定颂斜。
SwingUtilities可以解決這個(gè)矛盾。SwingUtilities的invokeLater和invokeAndWait方法可以將一個(gè)可執(zhí)行對(duì)象(Runnable)實(shí)例追加到EDT的可執(zhí)行隊(duì)列中拾枣。那么最后的代碼應(yīng)該是這樣的:

importBtn.addActionListener(newActionListener() {
            @Override
            public voidactionPerformed(ActionEvent e) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                           SwingUtilities.invokeLater(new Runnable() {
                                @Override
                                public voidrun() {
                                    lb.setText("1.檢查數(shù)據(jù)合法性...");
                                }
                            });
                           Thread.sleep(3000);//模仿檢測(cè)數(shù)據(jù)合法性
                           SwingUtilities.invokeLater(new Runnable() {
                                @Override
                                public voidrun() {
                                   lb.setText("2.正在導(dǎo)入數(shù)據(jù)...");
                                }
                            });
                           Thread.sleep(4000);//模仿導(dǎo)入數(shù)據(jù)
                            SwingUtilities.invokeLater(newRunnable() {
                                @Override
                                public voidrun() {
                                   lb.setText("3.導(dǎo)入成功!");
                                }
                            });
                        } catch(InterruptedException e1) {
                           e1.printStackTrace();
                        }
                    }
                }).start();
            }
        });

結(jié)論

通過(guò)上面內(nèi)容可以總結(jié)以下兩個(gè)swing編程原則:
所有的界面相關(guān)的更新沃疮,都應(yīng)該在 EDT 上執(zhí)行
而耗時(shí)的后臺(tái)運(yùn)行,不應(yīng)該在 EDT 上執(zhí)行

lambda表達(dá)式重寫(xiě)上面的代碼

 importBtn.addActionListener(e -> {
            new Thread(() -> {
                try {
                   SwingUtilities.invokeLater(() -> lb.setText("1.檢查數(shù)據(jù)合法性..."));
                    Thread.sleep(3000);//模仿檢測(cè)數(shù)據(jù)合法性
                    SwingUtilities.invokeLater(()-> lb.setText("2.正在導(dǎo)入數(shù)據(jù)..."));
                    Thread.sleep(4000);//模仿導(dǎo)入數(shù)據(jù)
                   SwingUtilities.invokeLater(() -> lb.setText("3.導(dǎo)入成功!"));
                } catch (InterruptedExceptione1) {
                    e1.printStackTrace();
                }
            }).start();
        });
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末梅肤,一起剝皮案震驚了整個(gè)濱河市司蔬,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌姨蝴,老刑警劉巖俊啼,帶你破解...
    沈念sama閱讀 221,548評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異左医,居然都是意外死亡授帕,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)浮梢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)跛十,“玉大人,你說(shuō)我怎么就攤上這事秕硝〗嬗常” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,990評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)奈偏。 經(jīng)常有香客問(wèn)我坞嘀,道長(zhǎng),這世上最難降的妖魔是什么惊来? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,618評(píng)論 1 296
  • 正文 為了忘掉前任丽涩,我火速辦了婚禮,結(jié)果婚禮上裁蚁,老公的妹妹穿的比我還像新娘内狸。我一直安慰自己,他們只是感情好厘擂,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著锰瘸,像睡著了一般刽严。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上避凝,一...
    開(kāi)封第一講書(shū)人閱讀 52,246評(píng)論 1 308
  • 那天舞萄,我揣著相機(jī)與錄音,去河邊找鬼管削。 笑死倒脓,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的含思。 我是一名探鬼主播崎弃,決...
    沈念sama閱讀 40,819評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼含潘!你這毒婦竟也來(lái)了饲做?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,725評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤遏弱,失蹤者是張志新(化名)和其女友劉穎盆均,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體漱逸,經(jīng)...
    沈念sama閱讀 46,268評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡泪姨,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了饰抒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片肮砾。...
    茶點(diǎn)故事閱讀 40,488評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖袋坑,靈堂內(nèi)的尸體忽然破棺而出唇敞,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 36,181評(píng)論 5 350
  • 正文 年R本政府宣布疆柔,位于F島的核電站咒精,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏旷档。R本人自食惡果不足惜模叙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望鞋屈。 院中可真熱鬧范咨,春花似錦、人聲如沸厂庇。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,331評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)权旷。三九已至替蛉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間拄氯,已是汗流浹背躲查。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,445評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留译柏,地道東北人镣煮。 一個(gè)月前我還...
    沈念sama閱讀 48,897評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像鄙麦,于是被迫代替她去往敵國(guó)和親典唇。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容