2010-03-30 56 views
4

我需要一種將UI指示符綁定到快速更改值的方法。Java/Swing:快速/慢速UI綁定問題

我有一個類NumberCruncher,它在一個關鍵的非UI線程中執行一堆繁重的處理,每秒循環數千次,其中一些會導致我關心的一組參數發生變化。 (將它們視爲鍵值存儲)

我想在UI線程中以較慢的速率顯示它們; 10-20Hz會很好。我如何添加MVC風格的通知,以便我的NumberCruncher代碼不需要知道UI代碼/綁定?

回答

5

執行此操作的習慣方法是使用SwingWorker類,並使用對publish(V...)的調用來定期通知Event Dispatch線程,以使其更新UI。

在以下Javadoc示例中,數字運算髮生在doInBackground()方法中的工作線程上,該方法在每次迭代時調用發佈。此調用使事件調度線程上的進程(V ...)方法異步調用,從而允許它更新UI。請注意,這可確保用戶交互界面始終從事件調度線程更新爲。另請注意,您可以選擇每N次迭代調用一次發佈,以降低用戶界面的更新頻率。

實施例從Javadoc中

class PrimeNumbersTask extends 
     SwingWorker<List<Integer>, Integer> { 
    PrimeNumbersTask(JTextArea textArea, int numbersToFind) { 
     //initialize 
    } 

    @Override 
    public List<Integer> doInBackground() { 
     while (! enough && ! isCancelled()) { 
       number = nextPrimeNumber(); 
       publish(number); 
       setProgress(100 * numbers.size()/numbersToFind); 
      } 
     } 
     return numbers; 
    } 

    @Override 
    protected void process(List<Integer> chunks) { 
     for (int number : chunks) { 
      textArea.append(number + "\n"); 
     } 
    } 
} 
+0

所以publish()和process()處理隊列?有趣.... – 2010-03-30 21:44:33

2

有一個對象,您的NumberCrucher根據您所做的衆多操作修改/保持更改。讓它運行在一個單獨的線程中。使用與NumberCruncher修改相同的Object的swing中有一個UI。這個線程只會在指定的時間段讀取值,所以它不應該是線程死鎖的問題。

NumberCruncher

public class NumberCruncher implements Runnable{ 
CommonObject commonObj; 
public NumberCruncher(CommonObject commonObj){ 
    this.commonObj = commonObj; 
} 
public void run() { 
    for(;;){ 
    commonObj.freqChangeVal = Math.random(); 
    } 
} 
} 

CommonObject:

public class CommonObject { 
public double freqChangeVal; 
} 

UI:

import javax.swing.JFrame; 
import javax.swing.JLabel; 
import javax.swing.JPanel; 

public class UI extends JFrame implements Runnable{ 

    private CommonObject commonObj = new CommonObject(); 

    JLabel label ; 

    public static void main(String args[]){ 
     UI ui = new UI(); 
     ui.begin(); 
     Thread t2 = new Thread(ui); 
     t2.start(); 
    } 

    private void begin(){ 
     JPanel panel = new JPanel(); 
     label = new JLabel("Test"); 
     panel.add(label); 

     Thread thread = new Thread(new NumberCruncher(commonObj)); 
     thread.start(); 

     this.add(panel); 
     this.setSize(200,200); 
     this.setVisible(true); 
    } 

    public void run() { 
     for(;;){ 
      try { 
       Thread.sleep(500); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
      label.setText(commonObj.freqChangeVal+""); 
      this.repaint(); 
     } 
    } 
} 
+0

-1:a)CommonObject的freqChangeVal字段不是易失性的,因此一個線程的更改可能在另一個線程上不可見。 b)UI run()方法在Event Dispatch線程上沒有被調用,這意味着JLabel正在被另一個線程修改(這不應該發生)。 c)這是重新發明車輪;更合適的機制是使用帶調用的SwingWorker來發布/處理,以便在Event Dispatch Thread上定期通知GUI。 – Adamski 2010-03-30 20:59:41

+0

@Adamski:感謝您花時間在我的程序中指出問題。 – bragboy 2010-03-31 09:12:44

+0

沒問題 - 你說得對,使用已經提供的任何類,併發是非常困難的。 – Adamski 2010-03-31 11:00:34

2

好像你MI ght想採取「聽衆」的方法。允許你的數字計算器註冊監聽器,然後每隔100-200個循環(可配置)(或在某種變化條件下),通知監聽器他們應該知道更新。

監聽器可以是另一個具有線程wait()的類,當它被通知時,它只是更新其內部變量,然後通知等待的線程。然後快速循環類可以快速更新外部值,而不用擔心訪問其快速更改的內部狀態。

wait()的另一個線程也可以在定時器線程上有一個wait(),該線程設置爲10-20HZ(可配置),以便在等待來自同步的下一次更新類。

3

SwingWorker,通過@Adamski建議的,是優選的;但javax.swing.Timer的一個實例是一個方便的替代方案,因爲「定時器的動作事件處理程序執行事件分派線程。」