淺談EventQueue in Swing
最近在看多線程的東西,EventQueue in Swing負責所有AWTEvent(以及其子類)的分發,以后如果要定義自己的ProgressBar可能會用到,先記下來。
EventQueue in Swing簡單工作原理
簡單來講,在EventQueue中有一個dispatchThread,這是一個線程類,負責事件的分發,當Queue中有事件的時候,它會摘取前面的事件并分發給相應的對象進行處理,等處理完之后再獲取下一個,當Queue中沒有事件的時候,線程等待。
當有事件觸發時,系統會調用EventQueue的push方法將AWTEvent添加到EventQueue的最后,同時喚醒dispatchThread。
為什么界面會死掉
所以可以看到,Swing的事件分發實際上是同步的,并且都是在dispatchThread這個線程中處理的,也就是說是一個事件一個事件處理的,如果有某一個事件處理的時間非常長的時侯,其他事件就會被堵塞在那里,從現象上看得話,就是界面會死掉,如果界面被其他窗口覆蓋之后再回到前面的時侯,會變成一片灰色,這是因為PaintEvent被堵塞而不能被分發出去的緣故。
為什么Modal Dialog(Frame)彈出的時候界面不會死
當在處理事件的時侯如果彈出一個Modal Dialog,那么處理方法會停在那里并等待Modal Dialog銷毀,這個時候按照上面的分析,dispatchThread也會停在那里,這樣的話其他事件也不會被分發,那么界面也應該會死掉才對。實際上在等待Modal Dialog銷毀的過程中,如果能夠保證事件可以順利地分發出去的話,界面當然就不會死。先來看這個例子。
- 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的例子中,當Button的Action被觸發,actionPerformed方法執行的時候,會首先幫助EventQueue分發事件,直到最少5秒之后返回,這時可以看到這個事件處理方法至少執行了5秒鐘,但是在這個過程中Dialog仍然可以正常工作,只是因為在這5秒之中并非是Sleep,而是在幫助EventQueue分發事件,如果代碼改成
Thread.sleep(5000);
的話,界面將會死掉。
所以在Modal Dialog彈出的時候,實際上只要在show方法中能夠實現類似上面的代碼,保證事件可以正常的分發(同時截獲父窗口的一些事件,過濾掉一些觸發Action的事件),那么父窗口的界面就不會死掉。
當事件處理方法很長時間才能做完該怎么辦
當事件處理方法需要很長時間才能執行完的話,如果需要保證界面不死的話,還是只能用多線程,雖然上面的方法實現了事件處理的時候界面不死,但是這和一般的事件處理是有不同的,上面的方法實際上在處理的時候什么都沒有做,而我們一般需要有自己的操作(比如訪問數據庫,訪問網絡,讀寫操作等需要很長時間才能處理完的工作),不可能做到一邊在操作一邊處理Event分發,這個時候只有新建一個線程才是正道。
不過關于很多EventQueue in Swing和EventDispatchThread的方法都被封裝在其實現里面,對外不可視,導致不可能對其進行一些修改,有點不爽。另外在EventQueue中的AWTEvent一般都是給最上層對象的,比如最上層的JDialog或者JFrame,然后由JDialog或者JFrame分發給其他的Component,不過我不知道怎么可以從AWTEvent事件找到真正的擁有者,這一點比較郁悶
【編輯推薦】