Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/320.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何使Swing模型与快速变化的;“真正的”;模型_Java_Swing_Multithreading - Fatal编程技术网

Java 如何使Swing模型与快速变化的;“真正的”;模型

Java 如何使Swing模型与快速变化的;“真正的”;模型,java,swing,multithreading,Java,Swing,Multithreading,众所周知,任何与Swing组件相关的操作都必须在上完成。这也适用于组件后面的,例如。在基本情况下很容易,但如果模型是必须在单独线程上运行的某个对象的“实时视图”,则情况会变得相当复杂,因为它变化很快。例如,JTable上的股票市场实时视图。股票市场通常不会在EDT上发生 那么,将必须在EDT上的Swing模型与必须随时随地可更新的“真实的”线程安全模型(de)耦合起来的更好模式是什么呢?一个可能的解决方案是实际分成两个单独的副本:“真实”模型加上它的Swing对应物,这是“真实”模型的快照。然后

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


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

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

通常的方法是发送“信号”UI侦听的某种类型。在我的代码中,我经常使用一个中央调度器,它发送的信号包含被修改的对象、字段/属性的名称以及新旧值。对于大小写
oldValue.equals(newValue)
oldValue.compareTo(newValue)==0
(后者用于日期和
BigDecimal

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


您可以将其转换为每个对象的侦听器,并让每个UI元素将自己注册到模型中。但我发现这只是将代码传播到了所有地方。当我两侧都有一组巨大的对象时,我有时只使用几个信号(或事件)来使事情更易于管理。

我可以推荐以下方法:

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

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);
    }
  }
}
公共类MyStockPanel扩展了JPanel{
私有最终阻止队列事件;
//在事件分派线程上调用Runnable并负责应用任何
//表模型的挂起事件。
private final Runnable processEventsRunnable=new Runnable(){
公开募捐{
StockEvent evt;
while((evt=stockEvents.poll()!=null){
//更新表格模型和火灾表格事件。
//可以通过触发单个表更改事件在此处进行优化
//如果处理大型事件,则队列为空。
}
}
}
//由事件分派线程以外的线程调用。将事件添加到
//“挂起”队列已准备好进行处理。
公开作废addStockEvent(StockEvent evt){
stockEvents.add(evt);
//优化1:仅当队列以前为空时才调用EDT
//正在添加此事件。如果此时大小为0,则EDT必须具有
//已处于活动状态并已从队列中删除事件,如果
//如果大于0,则我们知道EDT必须已在以前的
//方法调用,但尚未耗尽队列(即,因此无需调用它
//再次)。
if(stockEvents.size()==1){
//优化2:不要每次都创建新的Runnable,而是使用无状态
//内部类来排空队列并更新表模型。
调用器(processEventsRunnable);
}
}
}

Right:我不想在我的真实模型中实现Swing接口,因为我知道真实模型将从EDT以外的线程更新,这将违反Swing的要求。我认为您描述的本质上是“拆分模型”。如果与Swing的交互发生在EDT中,并且您的模型是线程安全的,那么从其他线程更新真实模型并不是一个大问题。Swing将在EDT中调用getValueAT()之类的方法,因此唯一需要担心的是发送通知(fireSmthChanged)在EDT中,您可以使用SwingUtilities.invokeLater进行操作……但当然,您将面临一些微妙的问题,例如树中的子级数目错误(因为在您向Swing发送通知后发生了更改),等等。这些微妙的问题是可以预料到的。但是请注意,如果Swing调用getValueAt(x)在实际模型中,如果该值刚才被另一个线程删除,则该值可能不再存在。因此,这可能会导致
ArrayIndexOutOfBoundsException
s,这并不微妙。这就是我实际上的意思