如何将Swing模型与快速变化的"真实"模型同步?

Joo*_*kka 16 java swing multithreading

众所周知,必须在事件派发线程上完成与Swing组件相关的任何事情.这也适用于组件背后的模型,例如TableModel.在基本情况下足够简单,但如果模型是必须在单独的线程上运行的事物的"实时视图",因为它正在快速变化,事情变得相当复杂.例如,JTable上股票市场的实时视图.股票市场通常不会发生在美国东部时间.

那么,什么是(de)耦合必须在EDT上的Swing模型的优选模式,以及必须随时随地更新的"真正的"线程安全模型?一种可能的解决方案是将模型实际拆分为两个单独的副本:"真实"模型加上其Swing对应物,这是"真实"模型的快照.然后它们会在EDT上同时(双向)同步.但这感觉就像臃肿.这真的是唯一可行的方法,还是有其他或更标准的方法?有用的图书馆 什么?

Ada*_*ski 11

我可以推荐以下方法:

  • 放置应该在"挂起事件"队列上修改表的事件,并且当事件放在队列上并且队列为空时,调用事件调度线程以排空所有事件的队列并更新表模型.此优化意味着您不再为接收到的每个事件调用事件调度线程,这解决了事件调度线程无法跟上基础事件流的问题.
  • 通过使用无状态内部类来排空表面板实现中的挂起事件队列,在调用事件派发线程时避免创建新的Runnable.
  • 可选的进一步优化:当耗尽挂起的事件队列时,通过记住需要重新绘制哪些表行然后在处理所有事件之后触发单个事件(或每行一个事件)来最小化触发的表更新事件的数量.

示例代码

public class MyStockPanel extends JPanel {
  private final BlockingQueue<StockEvent> stockEvents;

  // Runnable invoked on event dispatch thread and responsible for applying any
  // pending events to the table model.
  private final Runnable processEventsRunnable = new Runnable() {
    public void run() {
      StockEvent evt;

      while ((evt = stockEvents.poll() != null) {
        // Update table model and fire table event.
        // Could optimise here by firing a single table changed event
        // when the queue is empty if processing a large #events.
      }
    }
  }

  // Called by thread other than event dispatch thread.  Adds event to
  // "pending" queue ready to be processed.
  public void addStockEvent(StockEvent evt) {
    stockEvents.add(evt);

    // Optimisation 1: Only invoke EDT if the queue was previously empty before
    // adding this event.  If the size is 0 at this point then the EDT must have
    // already been active and removed the event from the queue, and if the size
    // is > 0 we know that the EDT must have already been invoked in a previous
    // method call but not yet drained the queue (i.e. so no need to invoke it
    // again).
    if (stockEvents.size() == 1) {
      // Optimisation 2: Do not create a new Runnable each time but use a stateless
      // inner class to drain the queue and update the table model.
      SwingUtilities.invokeLater(processEventsRunnable);
    }
  }
}
Run Code Online (Sandbox Code Playgroud)