Swing任務(wù)在Swing線程中執(zhí)行
Swing任務(wù)在Swing線程中執(zhí)行
界面顯示了一個(gè)null,因?yàn)轱@示代碼在查找代碼完成前被處理了。這是因?yàn)橐坏┬碌木€程啟動(dòng)了,代碼塊繼續(xù)執(zhí)行,而不是等待線程執(zhí)行完畢。這是那些奇怪的并發(fā)代碼塊中的一個(gè),下面將把它編寫到一個(gè)方法中使其能夠真正執(zhí)行。
在 SwingUtilities類中有兩個(gè)方法可以幫助我們解決這些問題:invokerLater()和invokeAndWait()。每一個(gè)方法都以一個(gè)Runnable作為參數(shù),并在Swing線程中執(zhí)行它。invokeAndWait()方法阻塞直到Runnnable執(zhí)行完畢;invokeLater()異步地執(zhí)行Runnable。invokeAndWait()一般不贊成使用,因?yàn)樗赡軐?dǎo)致嚴(yán)重的線程死鎖,對(duì)你的應(yīng)用造成嚴(yán)重的破壞。所以,讓我們把它放置一邊,使用invokeLater()方法。
要修正最后一個(gè)變量變量scooping和執(zhí)行順序的問題,我們必須將文本區(qū)域的getText()和setText()方法調(diào)用移入一個(gè)Runnable,只有在查詢結(jié)果返回后再執(zhí)行它,并且使Swing任務(wù)在Swing線程中執(zhí)行。我們可以這樣作,創(chuàng)建一個(gè)匿名Runnable傳遞給invokeLater(),包括在新線程的Runnable后的文本區(qū)域操作。這保證了 Swing代碼不會(huì)在查找結(jié)束之前執(zhí)行。下面是修正后的代碼:
- privatevoidsearchButton_actionPerformed(){
- outputTA.setText("Searchingfor:"+
- searchTF.getText());
- finalString[][]results=newString[1][1];
- newThread(){
- publicvoidrun(){
- //getresults.
- results[0]=lookup(searchTF.getText());
- //sendrunnabletotheSwingthread
- //therunnableisqueuedafterthe
- //resultsarereturned
- SwingUtilities.invokeLater(
- newRunnable(){
- publicvoidrun(){
- //Nowwe'reintheSwingthread
- outputTA.setText("");
- for(inti=0;
- i<results[0].length;
- i++){
- Stringresult=results[0][i];
- outputTA.setText(
- outputTA.getText()+
- ''+result);
- }
- }
- }
- );
- }
- }.start();
- }
這可以工作,但是這樣做令人非常頭痛。我們不得不對(duì)通過匿名線程執(zhí)行的順序,我們還不得不處理困難的scooping問題。問題并不少見,并且,這只是一個(gè)非常簡單的例子,我們已經(jīng)遇到了作用域,變量傳遞,和執(zhí)行順序等一系列問題。相像一個(gè)更復(fù)雜的問題,包含了幾層嵌套,共享的引用和指定的執(zhí)行順序。這種方法很快就失控了。
問題
我們?cè)谄髨D強(qiáng)制通過異步模型進(jìn)行同步執(zhí)行--企圖將一個(gè)方形的螺栓放到一個(gè)圓形的空中。只有我們嘗試這樣做,我們就會(huì)不斷地遭遇這些問題。從我的經(jīng)驗(yàn),可以告訴你這些代碼很難閱讀,很難維護(hù),并且易于出錯(cuò)。
這看起來是一個(gè)常見的問題,所以一定有標(biāo)準(zhǔn)的方式來解決,對(duì)嗎?出現(xiàn)了一些框架用于管理Swing的復(fù)雜性,所以讓我們來快速預(yù)覽一下它們可以做什么。
一個(gè)可以得到的解決方案是Foxtrot,一個(gè)由Biorn Steedom寫的框架,可以在SourceForge上獲取。它使用一個(gè)叫做Worker的對(duì)象來控制非Swing任務(wù)在非 Swing線程中的執(zhí)行,阻塞直到非Swing任務(wù)執(zhí)行完畢。它簡化了Swing線程,允許你編寫同步代碼,并在Swing線程和非Swing線程直接切換。下面是來自它的站點(diǎn)的一個(gè)例子:
- publicvoidactionPerformed(ActionEvente)
- {
- button.setText("Sleeping...");
- Stringtext=null;
- try
- {
- text=(String)Worker.post(newTask()
- {
- publicObjectrun()throwsException
- {
- Thread.sleep(10000);
- return"Slept!";
- }
- });
- }
- catch(Exceptionx)...
- button.setText(text);
- somethingElse();
- }
注意它是如何解決上面的那些問題的。我們能夠非常容易地在Swing線程中傳入傳出變量。并且,代碼塊看起來也很正確--先編寫的先執(zhí)行。但是仍然有一些問題障礙阻止使用從準(zhǔn)同步異步解決方案。Foxtrot中的一個(gè)問題是異常管理。使用Foxtrot,每次調(diào)用Worker必須捕獲Exception。這是將執(zhí)行代理給Worker來解決同步對(duì)異步問題的一個(gè)產(chǎn)物。
同樣以非常相似的方式,我此前也創(chuàng)建了一個(gè)框架,我稱它為鏈接運(yùn)行引擎(Chained Runnable Engine) ,同樣也遭受來自類似同步對(duì)異步問題的困擾。使用這個(gè)框架,你將創(chuàng)建一個(gè)將被引擎執(zhí)行的Runnable的集合。每一個(gè)Runnable都有一個(gè)指示器告訴引擎是否應(yīng)該在Swing線程或者另外的線程中執(zhí)行。引擎也保證Runnable以正確的順序執(zhí)行。所以Runnable #2將不會(huì)放入隊(duì)列直到Runnable #1執(zhí)行完畢。并且,它支持變量以HashMap的形式從Runnable到Runnable傳遞。
表面上,它看起來解決了我們的主要問題。但是當(dāng)你深入進(jìn)去后,同樣的問題又冒出來了。本質(zhì)上,我們并沒有改變上面描述的任何東西--我們只是將復(fù)雜性隱藏在引擎的后面。因?yàn)橹笖?shù)級(jí)增長的Runnable而使代碼編寫將變得非常枯燥,也很復(fù)雜,并且這些Runnable常常相互耦合。Runnable之間的非類型的HashMap變量傳遞變得難于管理。問題的列表還有很多。
在編寫這個(gè)框架之后,我意識(shí)到這需要一個(gè)完全不同的解決方案。這讓我重新審視了問題,看別人是怎么解決類似的問題的,并深入的研究了Swing的源代碼。
【編輯推薦】

















