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

2022-09-03 07:11:55

众所周知,与 Swing 组件相关的任何操作都必须在事件调度线程上完成。这也适用于组件背后的模型,例如 TableModel。在基本情况下很容易,但如果模型是必须在单独线程上运行的东西的“实时视图”,那么事情就会变得非常复杂,因为它变化很快。例如,JTable上股票市场的实时视图。股票市场通常不会发生在EDT上。

那么,将必须位于 EDT 上的 Swing 模型与必须随时随地更新的“真实”线程安全模型(去)耦合的更可取的模式是什么?一种可能的解决方案是将模型实际拆分为两个单独的副本:“真实”模型及其Swing对应物,这是“真实”模型的快照。然后,它们会不时地(双向)在 EDT 上同步。但这感觉就像膨胀一样。这真的是唯一可行的方法,还是有任何其他或更标准的方法?有用的图书馆?什么?


答案 1

我可以推荐以下方法:

  • 将应修改表的事件放在“挂起事件”队列上,当事件放在队列上且队列为空时,调用事件调度线程以清空队列中的所有事件并更新表模型。这种优化意味着您不再为收到的每个事件调用事件调度线程,这解决了事件调度线程无法跟上基础事件流的问题。
  • 通过使用无状态内部类来排出表面板实现中的挂起事件队列,避免在调用事件调度线程时创建新的 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);
    }
  }
}

答案 2

据我所知,您不想在实际模型中实现 Swing 模型接口,对吗?您能否将 Swing 模型实现为真实模型一部分的“视图”?它将它的读取访问getValueAt()转换为真实模型的调用,并且真实模型将通知Swing模型有关更改的信息,要么提供更改列表,要么假设Swing模型将负责查询它当前显示的所有内容的新值。