1. 程式人生 > >SwingUtilities中invokeLater和invokeAndWait的介紹

SwingUtilities中invokeLater和invokeAndWait的介紹

SwingUtilities中invokeLater和invokeAndWait介紹
   在Java中Swing是執行緒不安全的,是單執行緒的設計,這樣的造成結果就是:只能從事件派發執行緒訪問將要在螢幕上繪製的Swing元件。事件派發執行緒是呼叫paint和update等回撥方法的執行緒,它還是事件監聽器介面中定義的事件處理方法,例如,ActionListener中的actionPerformed方法在事件派發執行緒中呼叫。
   Swing是事件驅動的,所以在回撥函式中更新可見的GUI是很自然的事情,比如,有一個按鈕被按下,專案列表需要更新時,則通常在與該按鈕相關聯的事件監聽器的actionPerformed方法中來實現該列表的更新,從事件派發執行緒以外的執行緒中更新Swing元件是不正常的。
   有時需要從事件派發執行緒以外的執行緒中更新Swing元件,例如,在actionPerformed中有很費時的操作,需要很長時間才能返回,按鈕啟用後需要很長時間才能看到更新的列表,按鈕會長時間保持按下的狀態只到actionPerformed返回,一般說來耗時的操作不應該在事件處理方法中執行,因為事件處理返回之前,其他事件是不能觸發的,介面類似於卡住的狀況,所以在獨立的執行緒上執行比較耗時的操作可能更好,這會立即更新使用者介面和釋放事件派發執行緒去派發其他的事件。
   SwingUtilities類提供了兩個方法:invokeLate和invoteAndWait,它們都使事件派發執行緒上的可執行物件排隊。當可執行物件排在事件派發佇列的隊首時,就呼叫其run方法。其效果是允許事件派發執行緒呼叫另一個執行緒中的任意一個程式碼塊。
   只有從事件派發執行緒才能更新元件。
   程式示例:更新元件的錯誤方法
   startButton.addActionListener(new ActionListener() {
   public void actionPerformed(ActionEvent e) {
    GetInfoThread t = new GetInfoThread(Test.this);
    t.start();
    startButton.setEnabled(false);
   }
  });
  
  class GetInfoThread extends Thread {
 Test applet;

 public GetInfoThread(Test applet) {
  this.applet = applet;
 }

  public void run() {
   while (true) {
    try {
     Thread.sleep(500);
     applet.getProgressBar().setValue(Math.random() * 100);
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
   }
  }
 }
 錯誤分析:在actionPerformed中,監聽器把按鈕的允許狀態設定為false,由於是在事件派發執行緒上呼叫actionPerformed,所以setEnabled是一個有效的操作,但是在GetInfoThread中設定進度條是一個危險的做法,因為事件派發執行緒以外的執行緒更新了進度條,所以執行是不正常的。

   1、invokeLater使用
    class GetInfoThread extends Thread {
  Test applet;
 
  Runnable runx;
 
  int value;

  public GetInfoThread(final Test applet) {
   this.applet = applet;
   runx = new Runnable() {
    public void run() {
     JProgressBar jpb = applet.getProgressBar();
     jpb.setValue(value);
    }
   };
  }

   public void run() {
    while (true) {
     try {
      Thread.sleep(500);
      value = (int) (Math.random() * 100);
      System.out.println(value);
      SwingUtilities.invokeLater(runx);
     } catch (InterruptedException e) {
      e.printStackTrace();
     }
    }
   }
  }
   
   2、invokeAndWait
   與invoikeLater一樣,invokeAndWait也把可執行物件排入事件派發執行緒的佇列中,invokeLater在把可執行的物件放入佇列後就返回,而invokeAndWait一直等待知道已啟動了可執行的run方法才返回。如果一個操作在另外一個操作執行之前必須從一個元件獲得資訊,則invokeAndWait方法是很有用的。
   class GetInfoThread extends Thread {
   Runnable getValue,setValue;
   int value,currentValue;
   public GetInfoThread(final Test applet){
   getValue=new Runnable(){
   public void run(){
    JProgressBar pb=applet.getProgressBar();
    currentValue=pb.getValue();
    }
   };
   setValue=new Runnable(){
    public void run(){
     JProgressBar pb=applet.getProgressBar();
     pb.setValue(value);
    }
   }
   }
   public void run(){
    while(true){
    try{
    Thread.currentThead().sleep(500);
    value=(int)(Math.random()*100);
    try{
    SwingUtilities.invokeAndWait(getValue);//直到getValue可執行的run方法返回後才返回
      }catch(Exception ex){
      }
      if(currentValue!=value){
      SwingUtilities.invokeLater(setValue);
      }
     }
     }catch(Exception ex){
      }
    }
   }
   invokeLater和invoikeAndWait的一個重要區別:可以從事件派發執行緒中呼叫invokeLater,卻不能從事件派發執行緒中呼叫invokeAndWait,從事件派發執行緒呼叫invokeAndWait的問題是:invokeAndWait鎖定呼叫它的執行緒,直到可執行物件從事件派發執行緒中派發出去並且該可執行的物件的run方法啟用,如果從事件派發執行緒呼叫invoikeAndWait,則會發生死鎖的狀況,因為invokeAndWait正在等待事件派發,但是,由於是從事件派發執行緒中呼叫invokeAndWait,所以直到invokeAndWait返回後事件才能派發。
   ex)actionPerformed();返回的時候事件派發執行緒才能派發執行緒,而在actionPerformed中使用invokeAndWait則會導致actionPerformed不能返回。所以也就無法派發invokeAndWait中的執行緒。

由於Swing是執行緒不安全的,所以,從事件派發執行緒之外的執行緒訪問Swing元件是不安全的,SwingUtilities類提供這兩種方法用於執行事件派發執行緒中的程式碼