由于众所周知的原因,一般只有一个线程操作UI。对于多线程程序,怎么让工作线程去操作UI呢?在C++里,可以很方便地利用Windows消息,sendMessage和postMessage。在C#中,MSDN中也给出了用委托的方法。那Java中呢?

    Java中观察者模式非常常见。跨线程操作UI也可以用观察者模式来实现。UI线程作为观察者,当工作线程有“动静”时,主动通知UI线程。

   
   
   
   
  1. import java.util.Observable; 
  2. import java.util.Observer; 
  3.  
  4. public class MyAction extends Observable{ 
  5.  
  6.     public void test(){ 
  7.         this.setChanged(); 
  8.         this.notifyObservers(); 
  9.          
  10.     } 
  11.  

    被观察的对象“MyAction”通过test方法来通知观察者。MyAction应该在工作线程中。

   
   
   
   
  1. public class MainFrame extends JFrame implements Observer { 
  2.  
  3.     @Override 
  4.     protected void frameInit() { 
  5.         // TODO Auto-generated method stub 
  6.         super.frameInit(); 
  7.         this.setLayout(null); 
  8.         JButton jb = new JButton("test"); 
  9.         jb.setSize(100100); 
  10.         jb.addActionListener(new ActionListener() { 
  11.              
  12.             @Override 
  13.             public void actionPerformed(ActionEvent e) { 
  14.                 // TODO Auto-generated method stub 
  15.                 Thread t = new Thread(){ 
  16.  
  17.                     @Override 
  18.                     public void run() { 
  19.                         // TODO Auto-generated method stub 
  20.                         MyAction action = new MyAction(); 
  21. //注册观察者
  22.                         action.addObserver(MainFrame.this); 
  23.                         try { 
  24.                             this.sleep(5000); 
  25.                         } catch (InterruptedException e) { 
  26.                             // TODO Auto-generated catch block 
  27.                             e.printStackTrace(); 
  28.                         } 
  29.                         action.test(); 
  30.                     } 
  31.                      
  32.                 }; 
  33.                 t.start(); 
  34.             } 
  35.         }); 
  36.         this.add(jb); 
  37.     } 
  38.  
  39.     /** 
  40.      * @param args 
  41.      */ 
  42.     public static void main(String[] args) { 
  43.         // TODO Auto-generated method stub 
  44.         MainFrame f = new MainFrame(); 
  45.         f.setVisible(true); 
  46.         f.setDefaultCloseOperation(EXIT_ON_CLOSE); 
  47.         f.setSize(200200); 
  48.          
  49.     } 
  50.      
  51.     @Override 
  52.     public void update(Observable o, Object arg) { 
  53.         // TODO Auto-generated method stub 
  54.         JOptionPane.showMessageDialog(null"message"); 
  55.          
  56.     } 
  57.  

观察者收到通知时,update方法就会被调用,在这个方法里更新UI即可。

 

纠错:
      这种方法不能实现跨线程操作UI。测试方法,在update方法中获取Thread.currentThread().getId(),发现是非UI线程的ID。看了Observable的代码,确实没有跨线程的内容。
      不过下述JComponent方法是安全的,可以从任何线程调用:repaint()、revalidate()、和invalidate()。(repaint()和revalidate()方法为事件派发线程对请求排队,并分别调用paint()和validate()方法。invalidate()方法只在需要确认时标记一个组件和它的所有直接祖先。)所以,可以重写这些方法,来更新UI。
      另外,一般也不能使用Timer来主动获取工作线程的数据,因为Timer运行在其它线程上,不过可以参考http://jianshusoft.blog.51cto.com/2380869/764382。