代码之家  ›  专栏  ›  技术社区  ›  Joonas Pulakka

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

  •  16
  • Joonas Pulakka  · 技术社区  · 15 年前

    众所周知,任何与回转部件有关的操作都必须在 the event dispatch thread . 这也适用于 models 组件后面,例如 TableModel . 在基本情况下非常简单,但如果模型是必须在单独线程上运行的某个对象的“实时视图”,则情况会变得相当复杂,因为它变化很快。例如,jtable上股票市场的实时视图。股票市场通常不会发生在EDT上。

    那么,与必须在EDT上的Swing模型和必须随时随地可更新的“真正的”线程安全模型(de)耦合起来的首选模式是什么?一个可能的解决办法是 split the model 分为两个单独的副本:“真实”模型及其Swing对应物,这是“真实”模型的快照。然后(双向)在EDT上不时同步。但这感觉像是膨胀。这真的是唯一可行的方法吗,或者有其他更标准的方法吗?有帮助的图书馆?有什么事吗?

    3 回复  |  直到 15 年前
        1
  •  11
  •   Adamski    15 年前

    我可以推荐以下方法:

    • 将应修改表的事件放置在“挂起事件”队列上,以及将事件放置在队列上时 队列是空的 然后调用事件调度线程来清空所有事件的队列并更新表模型。这种优化意味着 您不再为收到的每个事件调用事件调度线程。 解决了事件调度线程与底层事件流不同步的问题。
    • 在调用事件调度线程时,避免创建新的可运行的,方法是使用无状态的内部类来排出表面板实现中挂起的事件队列。
    • 可选的进一步优化:当排出挂起的事件队列时,通过记住需要重新绘制的表行,然后在处理所有事件后触发单个事件(或每行一个事件),最小化激发的表更新事件数。

    示例代码

    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
  •  2
  •   Dmitry    15 年前

    据我所知,您不想在实际模型中实现Swing模型接口,是吗?你能把一个摆动模型作为一个真实模型的一部分的“视图”来实现吗?它将把它的read access getValueat()转换为real模型的调用,real模型将通知swing模型有关更改的信息,要么提供更改列表,要么假设swing模型将负责查询当前显示的所有内容的新值。

        3
  •  2
  •   Aaron Digulla    15 年前

    通常的方法是向用户界面发送某种类型的“信号”。在我的代码中,我经常使用一个中央调度器,它发送包含被修改的对象、字段/属性的名称加上新旧值的信号。没有为案例发送信号 oldValue.equals(newValue) oldValue.compareTo(newValue) == 0 (后者用于日期和 BigDecimal )

    然后,UI线程为所有信号注册一个侦听器。然后,它检查对象和名称,然后将其转换为通过 asyncExec() .

    您可以将其转换为每个对象的监听器,并让每个UI元素注册到模型中。但我发现这只是把代码散布到各处。当我在两侧都有一个巨大的对象集时,有时我只是使用几个信号(或事件)来使事情更易于管理。