Java 什么';在EDT中只运行一次代码的正确方法是什么?

Java 什么';在EDT中只运行一次代码的正确方法是什么?,java,swing,event-dispatch-thread,Java,Swing,Event Dispatch Thread,我有一个Swing应用程序,它使用Java线程不断地执行一些操作。此操作的结果将更新UI中图形的内容: class ExampleThread { ... public void run() { while (running) { // extract some information ... // show results in UI SwingUtilities

我有一个Swing应用程序,它使用Java线程不断地执行一些操作。此操作的结果将更新UI中图形的内容:

class ExampleThread {
    ... 
    public void run() {
        while (running) {
            // extract some information
            ...

            // show results in UI
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                     // use information to update a graph
                }
            });

            // sleep some seconds
            ...
        }
    }
    ...
}
我遇到的问题是,当EDT在其他操作中膨胀时。在这种情况下,
ExampleThread
可以注册各种更新图形的
Runnable
实例。对于我的应用程序来说,这将是一种浪费时间的行为,因为在显示结果之前,图表将被更新数次。我想要的是在每个EDT循环中最多运行一次
//更新图形
代码


我的问题是:确保
Runnable
只运行一次的正确方法是什么?

我建议使用
SwingWorker
并利用发布方法将更新发布回EDT。EDT将在下次计划时应用一个或多个更新。例如,假设您的计算结果是
Integer
实例:

// Boolean flag to determine whether background thread should continue to run.
AtomicBoolean running = new AtomicBoolean(true);

new SwingWorker<Void, Integer>() {
  public Void doInBackground() {
    while (running.get()) {
      // Do calculation
      Integer result = doCalculation();

      publish(result);
    }
  }

  protected void process(List<Integer> chunks) {
    // Update UI on EDT with integer results.
  }
}

或者,您可能希望研究使用
javax.swing.Timer

一个原子脏标志来实现这一点。这还有一个额外的好处,即仅在必要时添加到EDT队列。大假设—保存提取信息的数据结构是线程安全的,但这必须是正确的,才能使上述代码正常工作。如果不是,你应该考虑另一个答案中指出的摆动工人的方法;您还可以结合脏标志来防止冗余更新

AtomicBoolean dirty = new AtomicBoolean(false);
while (running) {
        // extract some information
        ...

        if (!dirty.getAndSet(true)) {            
          SwingUtilities.invokeLater(new Runnable() {
              public void run() {
                if (dirty.getAndSet(false)) {
                   // use information to update a graph
                }
              }
          });
        }

        // sleep some seconds
        ...
    }

没有必要找一个SwingWorker。编写自己的轻量级类就足够了。@mklhmnn:使用SwingWorker的“重量级”是什么?这正是它设计的目的。您将看到,我提供了一个替代的自产解决方案,这会导致代码不那么紧凑。SwingWorker通常是在后台线程和EDT之间工作的最佳方式。不过,我不确定上面的内容是如何解决冗余更新问题的。我忘了提到我一直使用JDK5。在这种情况下,第一种选择似乎是可行的。我想可能是有一个我不知道的功能在开箱即用。@Scott:每次调用publish并不一定会产生一个等价的调用process:SwingWorker使用内部队列以与我自己开发的解决方案完全相同的方式将结果分块在一起。您不需要显式标志;在添加新项之前,只需检查数据结构(例如队列)是否为空。如果为空,则表示EDT需要再次调度。为真-尽管这取决于数据结构。我尽可能少地假设;他可能没有一个更新对象队列,而是一个正在修改和呈现的全局状态。在解决问题时,我试图为上面的代码提供干扰最小的解决方案。一般来说,我也采用SwingWorker方法。
AtomicBoolean dirty = new AtomicBoolean(false);
while (running) {
        // extract some information
        ...

        if (!dirty.getAndSet(true)) {            
          SwingUtilities.invokeLater(new Runnable() {
              public void run() {
                if (dirty.getAndSet(false)) {
                   // use information to update a graph
                }
              }
          });
        }

        // sleep some seconds
        ...
    }