[转帖]Swing中的EventQueue

最近在看多线程的东西,Swing中的EventQueue负责所有AWTEvent(以及其子类)的分发,以后如果要定义自己的ProgressBar可能会用到,先记下来。

EventQueue简单工作原理

简单来讲,在EventQueue中有一个dispatchThread,这是一个线程类,负责事件的分发,当Queue中有事件的时候,它会摘取前面的事件并分发给相应的对象进行处理,等处理完之后再获取下一个,当Queue中没有事件的时候,线程等待。<o:p></o:p>

    当有事件触发时,系统会调用EventQueuepush方法将AWTEvent添加到EventQueue的最后,同时唤醒dispatchThread 

为什么界面会死掉

所以可以看到,Swing的事件分发实际上是同步的,并且都是在dispatchThread这个线程中处理的,也就是说是一个事件一个事件处理的,如果有某一个事件处理的时间非常长的时侯,其他事件就会被堵塞在那里,从现象上看得话,就是界面会死掉,如果界面被其他窗口覆盖之后再回到前面的时侯,会变成一片灰色,这是因为PaintEvent被堵塞而不能被分发出去的缘故。

为什么Modal Dialog(Frame)弹出的时候界面不会死

当在处理事件的时侯如果弹出一个Modal Dialog,那么处理方法会停在那里并等待Modal Dialog销毁,这个时候按照上面的分析,dispatchThread也会停在那里,这样的话其他事件也不会被分发,那么界面也应该会死掉才对。实际上在等待Modal Dialog销毁的过程中,如果能够保证事件可以顺利地分发出去的话,界面当然就不会死。先来看这个例子。

java 代码
  1. package eventqueue;   
  2.   
  3. import java.awt.AWTEvent;   
  4. import java.awt.ActiveEvent;   
  5. import java.awt.Component;   
  6. import java.awt.EventQueue;   
  7. import java.awt.MenuComponent;   
  8. import java.awt.event.ActionEvent;   
  9. import java.awt.event.ActionListener;   
  10.   
  11. import javax.swing.JButton;   
  12. import javax.swing.JDialog;   
  13.   
  14. public class TestEvent {   
  15.     public static void main(String[] args) {   
  16.         final JDialog dlg = new JDialog();   
  17.         dlg.setTitle("Test Event Queue");   
  18.         JButton btn = new JButton("Test");   
  19.         dlg.getContentPane().add(btn);   
  20.         btn.addActionListener(new ActionListener() {   
  21.             public void actionPerformed(ActionEvent e) {   
  22.                 long now = System.currentTimeMillis();   
  23.                 EventQueue theQueue = dlg.getToolkit().getSystemEventQueue();   
  24.                 System.out.println("at least 5000 millis");   
  25.                 while (System.currentTimeMillis() - now < 5000l) {   
  26.                     try {   
  27.                         // This is essentially the body of EventDispatchThread   
  28.                         AWTEvent event = theQueue.getNextEvent();   
  29.                         Object src = event.getSource();   
  30.                         if (event instanceof ActiveEvent) {   
  31.                             ((ActiveEvent) event).dispatch();   
  32.                         } else if (src instanceof Component) {   
  33.                             ((Component) src).dispatchEvent(event);   
  34.                         } else if (src instanceof MenuComponent) {   
  35.                             ((MenuComponent) src).dispatchEvent(event);   
  36.                         }   
  37.                     } catch (Exception ex) {   
  38.                         ex.printStackTrace();   
  39.                     }   
  40.                 }   
  41.                 System.out.println("end");   
  42.             }   
  43.         });   
  44.         dlg.pack();   
  45.         dlg.show();   
  46.     }   
  47. }   

<o:p>  </o:p>

在上面的例子中,当ButtonAction被触发,actionPerformed方法执行的时候,会首先帮助EventQueue分发事件,直到最少5秒之后返回,这时可以看到这个事件处理方法至少执行了5秒钟,但是在这个过程中Dialog仍然可以正常工作,只是因为在这5秒之中并非是Sleep,而是在帮助EventQueue分发事件,如果代码改成
    Thread.sleep(5000);
的话,界面将会死掉。

所以在Modal Dialog弹出的时候,实际上只要在show方法中能够实现类似上面的代码,保证事件可以正常的分发(同时截获父窗口的一些事件,过滤掉一些触发Action的事件),那么父窗口的界面就不会死掉。

当事件处理方法很长时间才能做完该怎么办

<o:p>当事件处理方法需要很长时间才能执行完的话,如果需要保证界面不死的话,还是只能用多线程,虽然上面的方法实现了事件处理的时候界面不死,但是这和一般的事件处理是有不同的,上面的方法实际上在处理的时候什么都没有做,而我们一般需要有自己的操作(比如访问数据库,访问网络,读写操作等需要很长时间才能处理完的工作),不可能做到一边在操作一边处理Event分发,这个时候只有新建一个线程才是正道。</o:p>

<o:p></o:p>

<o:p>  </o:p>

关于EventQueue的一些方法

  <o:p></o:p>

Window.getToolkit().getSystemEventQueue();
   
获取系统的EventQueue<o:p></o:p>

    SwingUtilities.isEventDispatchThread();
   
当前线程是否为EventDispatchThread<o:p></o:p>

    EventQueue.push(EventQueue queue);
   
将一个EventQueue作为当前EventQueuenextQueue,实际上事件是由最后一个EventQueue来分发的<o:p></o:p>

    EventQueue.getNextEvent();
   
获取下一个事件,如果没有,则等到有再返回<o:p></o:p>

   EventQueue.postEvent(AWTEvent theEvent);
  
添加一个Event<o:p></o:p>

 

不过关于很多EventQueueEventDispatchThread的方法都被封装在其实现里面,对外不可视,导致不可能对其进行一些修改,有点不爽。另外在EventQueue中的AWTEvent一般都是给最上层对象的,比如最上层的JDialog或者JFrame,然后由JDialog或者JFrame分发给其他的Component,不过我不知道怎么可以从AWTEvent事件找到真正的拥有者,这一点比较郁闷<o:p></o:p>

 

你可能感兴趣的:(多线程,thread,工作,swing)