以下两个简单的片段执行相同的操作,打印"Hello, world"
每一秒。但它们之间有什么区别呢?什么时候应该使用线程,什么时候应该使用时间轴。 Timeline 内部是否启动一个线程?如果没有,如何在不阻塞主线程的情况下每秒执行一次打印?
Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(1), e -> System.out.println("Hello, world")));
timeline.setCycleCount(-1);
timeline.play();
new Thread(() -> {
while (true) {
System.out.println("Hello, world!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
而且,在第二个片段中,如果我这样做Thread.sleep(some_volatile_variable_of_main_thread_that_changes_overtime)
,我怎样才能使用实现相同的功能Timeline
.
The Thread
类是标准 Java API 的一部分,代表后台线程。当一个Thread
启动后,其代码Runnable
's run
方法,或者它自己的方法run
方法,基本上与其他正在运行的线程并行执行。这允许可能需要很长时间才能运行的代码得以执行,而不会延迟可以“同时”运行的其他代码。使用此功能的程序员的代价是,如果数据在不同线程之间共享,则必须非常小心,以确保在任何单个线程中以一致的状态读取数据,并且数据是“活动的”:也就是说,数据会发生变化实际上,在一个线程中对数据所做的操作是在其他线程中观察到的。
相比之下,Timeline
是 JavaFX 框架的一部分,特别是其动画 API 的一部分。当 JavaFX 应用程序启动时,称为 FX 应用程序线程的线程开始运行。该线程在循环中运行,负责渲染 UI 和处理用户事件(除其他外)。 UI 渲染发生在“脉冲”上,(在当前版本中)目标是每秒发生 60 次。由于上面提到的数据同步问题,对 UI 的所有更改must在 FX 应用程序线程上进行。此外,FX 应用程序线程上的代码不得长时间运行(因此它不能通过sleep()
,或者通过 IO 读取大数据集),因为它会阻止线程渲染 UI。
A Timeline
通过拥有一组来工作KeyFrame
s,每个指定一个时间(以Duration
,自时间线开始以来测量)和事件处理程序和/或KeyValue
。在 FX 应用程序线程的每个脉冲上,如果Timeline
正在运行时,FX 应用程序线程循环将检查是否是时候触发任何事件处理程序。为了KeyValue
s,如果该值是可插值的(例如是数字,或者实现Interpolatable
,它的值将通过计算经过的时间占下一个时间的比例来计算KeyFrame
.
Timeline
s 对于简单的动画很有用(例如,通过在窗格中移动节点,通过使用KeyValue
指定其布局位置或平移坐标或类似的),也用于在特定时间执行一组离散的 UI 更新(例如,在“记忆”游戏中显示和隐藏图像)。
最后:
- 事件处理程序中的代码
KeyFrame
附于一个Timeline
可能会更新 UI,因为它保证在 FX 应用程序线程上执行
-
KeyValue
作为一部分更新的KeyFrame
in a Timeline
可能是屏幕上显示的 UI 元素的属性
- 事件处理程序中的代码
KeyFrame
s 不得阻止执行或执行长时间运行的任务
- 尝试使用
Timeline
在非 JavaFX 应用程序(即 JavaFX 运行时尚未启动的应用程序)中的更新将会失败,因为没有 JavaFX 应用程序线程来执行更新。
反过来:
- 代码在后台运行
Thread
must not更新 UI 元素(或其属性)。 JavaFX 和 Java Swing/AWT 都是如此
- 在后台线程上运行的代码可能会阻塞或执行长时间运行的任务
请注意,java.util.Timer
和它相关联TimerTask
(这是标准 Java API 的一部分)在由内部创建的后台线程上执行Timer
。这意味着,尽管 APITimer
and TimerTask
看起来有点类似于Timeline
,它们必须遵守后台线程的规则(不得更新 UI 等)。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)