4

我很好奇 javafx.concurent 和 Platform.runLater 在多线程 JavaFx 编程中到底有什么区别。

这是否意味着使用 javafx.concurrent,我们可以有多个实际的绘图线程,还是最终都在一个线程上?

例如,我喜欢的一件事是使用 JavafX 并同时使用 swing,因为它们都使用了 2 个不同的绘图线程。我会使用 swing 来处理繁重的东西(例如打开 FileChooser),并使用 JavaFX 来处理核心视觉内容,例如播放无缝的循环视频。然而,由于无头异常错误,mac 使这成为不可能,所以一切都落在了 javafx 上,这意味着在做诸如打开文件选择器之类的事情时会有很多暂停。

如果我使用 javafx.concurrent 重写我的应用程序,我是否可以像以前使用 Swing + JavaFX 那样模仿 2 绘制线程体验?

4

1 回答 1

14

Platform.runLater

AWorker与 互补Platform.runLater

  • Platform.runLater当您在 JavaFX 应用程序线程之外执行并且想要在 JavaFX 应用程序线程上运行一些逻辑时使用。
  • Worker您在 JavaFX 应用程序线程上运行并希望在新线程上产生一些逻辑或(尤其是)I/O 时使用 a,这样您就不会阻塞 JavaFX 应用程序线程。

您永远不会想在 a 的方法中进行网络 I/O Platform.runLaterrun但通常希望在 aWorkercall方法中进行。

任务与服务

考虑使用Worker的TaskService子类。这些是FutureTask的 JavaFX 包装器(又是Runnable)。Worker 提供了一个调用方法来在后台线程上运行逻辑。它们维护执行状态(通过向 JavaFX 线程发出线程安全回调通知以进行状态更改)并通过valuemessageexception属性返回调用结果。

Task利用和javadoc 示例中的设计模式Service来简化线程安全应用程序的创建,这些应用程序具有以下特性:

  • 为 UI 更新异步获取数据。
  • 任务进度的定期消息更新。
  • 构建尚未附加到显示场景的节点图。
  • 通过进度条等监控进度。

一起使用 Workers 和 Platform.runLater

此外,TaskandService的使用与Platform.runLater. 例如,如果您有一个很长的运行Task时间,您希望定期或在缓冲区填充时将部分结果返回到 UI,那么Platform.runLater在任务的call方法中执行就是执行此操作的方法。

使用现有的线程系统

当您没有库提供的现有线程服务,而是创建自己的线程以在后台执行时,工作者很有用。如果您有一个现有的线程服务,那么您将需要使用Platform.runLater在 JavaFX 应用程序线程上执行逻辑。

小心编写多线程代码

请注意,即使您使用Worker. 您仍然必须注意不要违反标准 JavaFX 并发规则,例如从不更新活动场景图中的节点(包括不更新活动场景图中的节点绑定到的值 - 例如支持的可观察项目列表一个列表视图)。

回答你的一些附加问题

这是否意味着使用 javafx.concurrent,我们可以有多个实际的绘图线程,还是最终都在一个线程上?

JavaFX 中只有一个渲染线程。您不能使用 JavaFX 并发创建更多渲染线程。您可以使用许多线程在 JavaFX 线程之外创建节点或将像素设置为屏幕外的 WriteableImage 或 Canvas,但最终每个渲染操作都通过由 JavaFX 系统管理的单个线程,您无法控制该线程。

如果我使用 javafx.concurrent 重写我的应用程序,我是否可以像以前使用 Swing + JavaFX 那样模仿 2 绘制线程体验?

不,即使你可以,我也不认为这是可取的。使用这样的模型,很容易创建微妙的、难以调试的线程处理相关错误。您可能从这种设置中看到的收益可能比您希望或预期的要少。

请参阅相关文章,了解为什么不建议使用 2 个或更多“绘制线程”:

Java 8 正在添加一个实验性命令行开关,以便为 JavaFX 应用程序线程和 Swing 事件调度线程使用相同的线程。这样做的主要原因是它简化了编程模型。

一切都落在了 javafx 上,这意味着在做诸如打开文件选择器之类的事情时会有很多停顿。

也许您的代码效率低下(例如在 UI 线程上执行 I/O)导致了暂停。

繁重的东西(例如打开 FileChooser)

打开和渲染 FileChooser 并不繁重。JavaFX 可以轻松处理此类操作而不会降低性能。耗时的是与 I/O 相关的东西,例如递归地遍历大型文件树以获取文件属性。在这种情况下,您可以做的是为 I/O 生成一个线程,在 a 中运行它,Worker并定期通过Platform.runLater. 这样的方案会运作良好。瓶颈不是绘图,因此拥有另一个绘图线程没有任何优势。瓶颈是缓慢的 I/O 系统,通过使用单独的 I/O 线程来缓解这个瓶颈,这样主 UI 线程不会受到影响,并且用户不会在 I/O 发生时遇到 UI 冻结。

于 2013-09-10T04:36:44.033 回答