共享数据模型

Swing 的表现对象(包括TableModel 和TreeModel等数据模型)都被封闭在事件线程中。在简单的GUI程序中,所有的可变状态都被保存在表现对象中,并且除了事件线程之外,唯一的线程就是主线程。要在这些程序中强制实施单线程规则是很容易的:不要从主线程中访问数据模型或表现组件。在一些更复杂的程序中,可能会使用其他线程对持久化的存储(例如文件系统、数据库等)进行读写操作以免降低系统的响应性。

最简单的情况是,数据模型中的数据由用户来输入或者由应用程序在启动时静态地从文件或其他数据源加载。在这种情况下,除了事件线程之外的任何线程都不可能访问到数据。但在某些情况下,表现模型对象只是一个数据源(例如数据库、文件系统或远程服务等)的视图对象。这时,当数据在应用程序中进出时,有多个线程都可以访问这些数据。

例如,你可以使用一个树形控件来显示远程文件系统的内容。在显示树形控件之前,并不需要枚举整个文件系统――那样做会消耗大量的时间和内存。正确的做法是,当树节点被展开时才读取相应的内容。即使只枚举远程卷上的单个目录也可能花费很长的时间,因此你可以考虑在后台任务中执行枚举操作。当后台任务完成后,必须通过某种方式将数据填充到树形模型中。可以使用线程安全的树形模型来实现这个功能:通过invokeLater提交一个任务,将数据从后台任务中“推入”事件线程,或者让事件线程池通过轮询来查看是否有数据可用。

  线程安全的数据模型

只要阻塞操作不会过度地影响响应性,那么多个线程操作同一份数据的问题都可以通过线程安全的数据模型来解决。如果数据模型支持细粒度的并发,那么事件线程和后台线程就能共享该数据模型,而不会发生响应性问题。例如,第5章的DelegatingVehicleTracker在底层使用了一个ConcurrentHashMap来提供高度并发的读写操作。这种方法的缺点在于,ConcurrentHashMap无法提供一致的数据快照,而这可能是需求的一部分。线程安全的数据模型必须在更新模板时产生事件,这样视图才能在数据发生变化后进行更新。

有时候,在使用版本化数据模型时,例如CopyOnWriteArrayList[CPJ2.2.3.3],可能要同时获得线程安全性、一致性以及良好的响应性。当获取一个“写时拷贝(Copy-On-Write)”容器的迭代器时,这个迭代器将遍历整个容器。然而,只有在遍历操作远远多于修改操作时,“写时拷贝”容器才能提供更好的性能,例如在车辆追踪应用程序中就不适合采用这种方法。一些特定的数据结构或许可以避免这种限制,但要构建一个既能提供高效的并发访问又能在旧数据无效后不再维护它们的数据结构却并不容易,因此只有其他方法都行不通后才应该考虑使用它。

   分解数据模型

从GUI的角度看, Swing的表格模型类,例如TableModel和TreeModel,都是保存将要显示的数据的正式方法。然而,这些模型对象本身通常都是应用程序中其他对象的“视图”。如果在程序中既包含用于表示的数据模型,又包含应用程序特定的数据模型,那么这种应用程序就被称为拥有一种分解模型设计(Fowler,2005)。

在分解模型设计中,表现模型被封闭在事件线程中,而其他模型,即共享模型,是线程安全的,因此既可以由事件线程方法,也可以由应用程序线程访问。表现模型会注册共享模型的监听器,从而在更新时得到通知。然后,表示模型可以在共享模型中得到更新:通过将相关状态的快照嵌入到更新消息中,或者由表现模型在收到更新事件时直接从共享模型中获取数据。

快照这种方法虽然简单,但却存在着一些局限。当数据模型很小,更新频率不高,并且这两个模型的结构相似时,它可以工作得良好。如果数据模型很大,或者更新频率极高,在分解模型包含的信息中有一方或双方对另一方不可见,那么更高效的方式是发送增量更新信息而不是发送一个完整的快照。这种方法将共享模型上的更新操作序列化,并在事件线程中重现。增量更新的另一个好处是,细粒度的变化信息可以提高显示的视觉效果----如果只有一辆车移动,那么只需更新发生变化的区域,而不用重绘整个显示图形。

如果一个数据模型必须被多个线程共享,而且由于阻塞、一致性或复杂度等原因而无法实现一个线程安全的模型时,可以考虑使用分解模型设计。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

知一NN

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值