淺談EventQueue in Swing
最近在看多線程的東西,EventQueue in Swing負(fù)責(zé)所有AWTEvent(以及其子類)的分發(fā),以后如果要定義自己的ProgressBar可能會(huì)用到,先記下來。
EventQueue in Swing簡(jiǎn)單工作原理
簡(jiǎn)單來講,在EventQueue中有一個(gè)dispatchThread,這是一個(gè)線程類,負(fù)責(zé)事件的分發(fā),當(dāng)Queue中有事件的時(shí)候,它會(huì)摘取前面的事件并分發(fā)給相應(yīng)的對(duì)象進(jìn)行處理,等處理完之后再獲取下一個(gè),當(dāng)Queue中沒有事件的時(shí)候,線程等待。
當(dāng)有事件觸發(fā)時(shí),系統(tǒng)會(huì)調(diào)用EventQueue的push方法將AWTEvent添加到EventQueue的最后,同時(shí)喚醒dispatchThread。
為什么界面會(huì)死掉
所以可以看到,Swing的事件分發(fā)實(shí)際上是同步的,并且都是在dispatchThread這個(gè)線程中處理的,也就是說是一個(gè)事件一個(gè)事件處理的,如果有某一個(gè)事件處理的時(shí)間非常長(zhǎng)的時(shí)侯,其他事件就會(huì)被堵塞在那里,從現(xiàn)象上看得話,就是界面會(huì)死掉,如果界面被其他窗口覆蓋之后再回到前面的時(shí)侯,會(huì)變成一片灰色,這是因?yàn)镻aintEvent被堵塞而不能被分發(fā)出去的緣故。
為什么Modal Dialog(Frame)彈出的時(shí)候界面不會(huì)死
當(dāng)在處理事件的時(shí)侯如果彈出一個(gè)Modal Dialog,那么處理方法會(huì)停在那里并等待Modal Dialog銷毀,這個(gè)時(shí)候按照上面的分析,dispatchThread也會(huì)停在那里,這樣的話其他事件也不會(huì)被分發(fā),那么界面也應(yīng)該會(huì)死掉才對(duì)。實(shí)際上在等待Modal Dialog銷毀的過程中,如果能夠保證事件可以順利地分發(fā)出去的話,界面當(dāng)然就不會(huì)死。先來看這個(gè)例子。
- packageeventqueue;
 - importjava.awt.AWTEvent;
 - importjava.awt.ActiveEvent;
 - importjava.awt.Component;
 - importjava.awt.EventQueue;
 - importjava.awt.MenuComponent;
 - importjava.awt.event.ActionEvent;
 - importjava.awt.event.ActionListener;
 - importjavax.swing.JButton;
 - importjavax.swing.JDialog;
 - publicclassTestEvent{
 - publicstaticvoidmain(String[]args){
 - finalJDialogdlg=newJDialog();
 - dlg.setTitle("TestEventQueue");
 - JButtonbtn=newJButton("Test");
 - dlg.getContentPane().add(btn);
 - btn.addActionListener(newActionListener(){
 - publicvoidactionPerformed(ActionEvente){
 - longnow=System.currentTimeMillis();
 - EventQueuetheQueue=dlg.getToolkit().getSystemEventQueue();
 - System.out.println("atleast5000millis");
 - while(System.currentTimeMillis()-now<5000l){
 - try{
 - //ThisisessentiallythebodyofEventDispatchThread
 - AWTEventevent=theQueue.getNextEvent();
 - Objectsrc=event.getSource();
 - if(eventinstanceofActiveEvent){
 - ((ActiveEvent)event).dispatch();
 - }elseif(srcinstanceofComponent){
 - ((Component)src).dispatchEvent(event);
 - }elseif(srcinstanceofMenuComponent){
 - ((MenuComponent)src).dispatchEvent(event);
 - }
 - }catch(Exceptionex){
 - ex.printStackTrace();
 - }
 - }
 - System.out.println("end");
 - }
 - });
 - dlg.pack();
 - dlg.show();
 - }
 - }
 
在上面Swing的例子中,當(dāng)Button的Action被觸發(fā),actionPerformed方法執(zhí)行的時(shí)候,會(huì)首先幫助EventQueue分發(fā)事件,直到最少5秒之后返回,這時(shí)可以看到這個(gè)事件處理方法至少執(zhí)行了5秒鐘,但是在這個(gè)過程中Dialog仍然可以正常工作,只是因?yàn)樵谶@5秒之中并非是Sleep,而是在幫助EventQueue分發(fā)事件,如果代碼改成
Thread.sleep(5000);
的話,界面將會(huì)死掉。
所以在Modal Dialog彈出的時(shí)候,實(shí)際上只要在show方法中能夠?qū)崿F(xiàn)類似上面的代碼,保證事件可以正常的分發(fā)(同時(shí)截獲父窗口的一些事件,過濾掉一些觸發(fā)Action的事件),那么父窗口的界面就不會(huì)死掉。
當(dāng)事件處理方法很長(zhǎng)時(shí)間才能做完該怎么辦
當(dāng)事件處理方法需要很長(zhǎng)時(shí)間才能執(zhí)行完的話,如果需要保證界面不死的話,還是只能用多線程,雖然上面的方法實(shí)現(xiàn)了事件處理的時(shí)候界面不死,但是這和一般的事件處理是有不同的,上面的方法實(shí)際上在處理的時(shí)候什么都沒有做,而我們一般需要有自己的操作(比如訪問數(shù)據(jù)庫,訪問網(wǎng)絡(luò),讀寫操作等需要很長(zhǎng)時(shí)間才能處理完的工作),不可能做到一邊在操作一邊處理Event分發(fā),這個(gè)時(shí)候只有新建一個(gè)線程才是正道。
不過關(guān)于很多EventQueue in Swing和EventDispatchThread的方法都被封裝在其實(shí)現(xiàn)里面,對(duì)外不可視,導(dǎo)致不可能對(duì)其進(jìn)行一些修改,有點(diǎn)不爽。另外在EventQueue中的AWTEvent一般都是給最上層對(duì)象的,比如最上層的JDialog或者JFrame,然后由JDialog或者JFrame分發(fā)給其他的Component,不過我不知道怎么可以從AWTEvent事件找到真正的擁有者,這一點(diǎn)比較郁悶
【編輯推薦】















 
 
 
 
 
 
 