简体   繁体   English

Java / Swing:快速/慢速UI绑定问题

[英]Java/Swing: the fast/slow UI binding problem

I need a way to bind UI indicators to rapidly-changing values. 我需要一种方法将UI指标绑定到快速变化的值。

I have a class NumberCruncher which does a bunch of heavy processing in a critical non-UI thread, thousands of iterations of a loop per second, and some number of those result in changes to a set of parameters I care about. 我有一个NumberCruncher类,它在一个关键的非UI线程中进行大量繁重的处理,每秒循环数千次迭代,其中一些导致我关注的一组参数的更改。 (think of them as a key-value store) (把它们想象成一个键值存储)

I want to display those at a slower rate in the UI thread; 我想在UI线程中以较慢的速率显示那些; 10-20Hz would be fine. 10-20Hz就好了。 How can I add MVC-style notification so that my NumberCruncher code doesn't need to know about the UI code/binding? 如何添加MVC样式的通知,以便我的NumberCruncher代码不需要知道UI代码/绑定?

The idiomatic way to do this is to use the SwingWorker class, and to use calls to publish(V...) to notify the Event Dispatch thread periodically causing it to update the UI. 这样做的惯用方法是使用SwingWorker类,并使用调用publish(V...)来定期通知Event Dispatch线程,使其更新UI。

In the below example taken from the Javadoc the number crunching takes place on a worker thread in the doInBackground() method, which calls publish on each iteration. 在以下来自Javadoc的示例中,数字运算在doInBackground()方法中的工作线程上进行,该方法在每次迭代时调用publish。 This call causes the process(V...) method to be called asynchronously on the Event Dispatch thread allowing it to update the UI. 此调用导致在Event Dispatch线程上异步调用process(V ...)方法,允许它更新UI。 Note that this ensures that the user interaface is always updated from the Event Dispatch thread . 请注意,这可确保始终从Event Dispatch线程更新用户交互。 Also note that you may choose to call publish every N iterations to reduce the frequency at which the user interface is updated. 另请注意,您可以选择每N次迭代调用发布,以降低用户界面更新的频率。

Example From Javadoc 来自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");
         }
     }
 }

SwingWorker , suggested by @Adamski, is preferable; @Adamski建议的SwingWorker更可取; but an instance of javax.swing.Timer is a convenient alternative for this, as "the action event handlers for Timers execute [on] the event-dispatching thread." 但是javax.swing.Timer一个实例是一个方便的替代javax.swing.Timer ,因为“Timers的动作事件处理程序执行事件派发线程。”

Have a single object which your NumberCrucher modifies/keeps on changing based on the numerous operations you do. 根据您执行的众多操作,让NumberCrucher修改/保持一个对象。 Let that run in a separate thread. 让它在一个单独的线程中运行。 Have a UI in swing which uses the same Object that NumberCruncher modifies. 在swing中使用与NumberCruncher修改的相同Object的UI。 This thread is going to only read the values at specified time period so it should not be a problem of thread deadlocks. 此线程将仅在指定的时间段读取值,因此它不应该是线程死锁的问题。

NumberCruncher NumberCruncher

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

CommonObject: 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();
        }
    }
}

Seems like you might want to take the "Listener" approach. 好像你可能想要采用“倾听者”方法。 Allow your number cruncher to register listeners, then every 100-200 loops (configurable) (or on some change condition), notify the listeners that there is an update they should be aware of. 允许您的数字cruncher注册侦听器,然后每100-200个循环(可配置)(或在某些更改条件下),通知侦听器他们应该知道有更新。

The listener can be another class that has a thread wait() ing on it, and when it gets notified, it just updates its internal variable, then notifies the waiting thread. 监听器可以是另一个在其上有线程wait()的类,当它被通知时,它只是更新其内部变量,然后通知等待的线程。 The fast loop class then has a quick way to update an external value and not worry about access to its fast changing internal state. 然后,快速循环类可以快速更新外部值,而不用担心访问其快速变化的内部状态。

The other thread that wait()s can also have a wait() on a timer thread that is set to 10-20HZ (configurable) to wait on the timer before wait()ing on the next update from your synchronized class. wait()s的另一个线程也可以在一个定时器线程上有一个wait(),该线程设置为10-20HZ(可配置),在等待()同步类的下一次更新之前等待定时器。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM