【发布时间】:2016-01-08 21:50:24
【问题描述】:
我必须说我是 Java 和 JavaFX 的新手(不到 2 个月),而且我的 UML 技能并不完美,所以我可能在图表上使用了错误的块或/和连接器 :) 但我希望你有一个想法。
我有一个订单管理应用,其设计如下(简化为必需品):
简单地说,JavaFX GUI 在表格视图中显示客户端(发送订单)和代理(在网络连接的另一端)之间发生的事情(即订单的当前状态)。 Order Manager 是唯一有权修改模型(即订单列表及其字段)的实体,并且修改模型的所有方法都是同步的(因此从任何线程调用它们都是安全的)。
订单是 JavaFX bean,不同的字段实现为 JavaFX 属性。这些属性绑定到 GUI 中的表列,因此每当订单管理器更新字段时,更改都会通过绑定机制传播到 GUI。
现在,因为属性绑定机制不是线程安全的(参见下面的rule:
应用程序必须将节点附加到场景,并在 JavaFX 应用程序线程上修改已附加到场景的节点。
),我必须在 Platform.runLater() 调用中包装所有修改这些字段的代码,例如:
public void onOrderCanceled(int id, String reason) {
Order order = orderbook.get(id);
if(order == null) {
throw new IllegalArgumentException("Order "+id+ " not found");
}
Platform.runLater(() -> {
order.setReason(reason);
order.setStated(CANCELED);
subscribers.foreach(sub -> sub.notifyUpdated(order));
});
}
这种方法有以下不愉快的影响:
- 客户端通知延迟任意时间(直到 GUI 线程完成其消息队列的处理)。原因:订单字段更新前无法通知客户端(否则数据不正确),只能在GUI线程中更新字段。
- 由于订单状态不会立即修改,而是在未来某个时间点,订单对象与实际订单状态之间存在一段时间的不一致。
- 如果 GUI 线程被阻塞或变得非常慢(由于错误或设计缺陷),客户端代码会被阻塞或变慢(在等待 GUI 线程的消息队列中卡住的通知时)。
有没有更好的方法呢?理想情况下,我想要一个解决方案:
- 允许客户端代码尽可能快地与网络层通信(通过订单管理器),即无需等待 GUI 跟上
- 允许 GUI 稍有滞后,但不得“跳过”字段更新,或者至少永远不要放弃最近的更新(这是最相关的)
- 依靠FX属性绑定架构更新GUI(我觉得很方便)
我觉得我需要为仅在 FX 线程中更新的 GUI 创建另一个“模型”,而“真实”模型将由订单管理器和客户端代码使用,我需要确保保持两个模型同步(这令人毛骨悚然)。
FX 的设计是否考虑了多线程?我查看了任务和服务接口,但看起来不合适(在我的情况下,GUI 不会启动任务 - 它来自外部源、客户端代码或网络)。
提前致谢!
【问题讨论】:
-
您能解释一下 Task 或 Service 类没有帮助吗?
-
@JSamir 因为 Task 旨在为在后台运行并返回结果的长异步操作建模。就我而言,该应用程序大部分时间只是坐着等待外部事件。请注意,与代理的通信不是严格的请求-响应,可能存在没有任何请求的“响应”(通知)。我可能很笨,但我只是不明白如何在这里应用它,将什么封装为任务......
-
当你说应用正在等待外部事件时,这些外部事件和你的应用是如何交互的?
-
@ItachiUchiha 有两种类型的事件:来自代理的消息(由最终调用订单管理器方法的专用网络线程处理)和来自客户端代码的操作(它在单独的线程中运行,并在满足某些条件时调用订单管理器的方法)
-
sub.notifyUpdated(order);是做什么的?我猜它正在通知客户。据我了解,您实际上并不需要在 UI 线程上执行此操作,您只需要确保在 UI 上发生相同的更新,并且以相同的顺序,对吗?
标签: java multithreading user-interface javafx