程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> SwingUtilities中invokeLater和invokeAndWait的介紹

SwingUtilities中invokeLater和invokeAndWait的介紹

編輯:關於JAVA

在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類提供這兩種方法用於執行事件派發線程中的代 碼

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved