Issue
The following two simple snippets do the same, print "Hello, world"
each second. But what's the difference between them? When should I use a thread and when should I use Timeline. Does Timeline internally start a thread? if it doesn't, how the print gets executed each one second without blocking the main thread?
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();
And, In the second snippet, if I do Thread.sleep(some_volatile_variable_of_main_thread_that_changes_overtime)
, How can I achieve the same functionality using Timeline
.
Solution
The Thread
class is part of the standard Java API, and represents a background thread. When a Thread
is started, the code in either its Runnable
's run
method, or its own run
method, executes essentially in parallel to other running threads. This allows code that may take a long time to run to execute without delaying other code that can be run "at the same time". The cost to the programmer of using this is that great care must be taken if data is shared between different threads, to ensure it is read in a consistent state in any single thread, and that data is "live": that is, that changes made to data in one thread are actually observed in other threads.
By contrast, Timeline
is part of the JavaFX framework, and in particular part of its animation API. When a JavaFX application is started, a thread called the FX Application Thread starts running. This thread runs in a loop and is responsible for rendering the UI and processing user events (among other things). The UI rendering occurs on a "pulse", which is (in the current version) targeted to occur 60 times per second. Because of the data synchronization issues alluded to above, all changes to the UI must be made on the FX Application Thread. Additionally, code on the FX Application thread must not be long-running (so it cannot pause with a sleep()
, or read large data sets over IO), because it would block the thread from rendering the UI.
A Timeline
works by having a set of KeyFrame
s, each of which specify a time (in the form of a Duration
, measured since the timeline started) and an event handler and/or a KeyValue
. On each pulse of the FX Application Thread, if a Timeline
is running, the FX Application Thread loop will check if it is time to trigger any event handlers. For KeyValue
s, if the value is interpolatable (e.g. is numeric, or implements Interpolatable
, it's value will be calculated by computing the time elapsed as a proportion of the time for the next KeyFrame
.
Timeline
s are useful for simple animations (by, e.g., moving a node across a pane, by using KeyValue
s that specify it's layout position or translation coordinates, or similar), and also for performing a discrete set of UI updates at specific times (e.g. showing and hiding images in a "memory" game).
Consequently:
- Code in event handlers for a
KeyFrame
attached to aTimeline
may update the UI, since it is guaranteed to be executed on the FX Application Thread KeyValue
s that are updated as part of aKeyFrame
in aTimeline
may be properties of UI elements that are displayed on the screen- Code in event handlers for
KeyFrame
s must not block execution or perform long-running tasks - Trying to use a
Timeline
in a non-JavaFX application (i.e. one where the JavaFX runtime has not been started) will fail, because there will be no JavaFX Application Thread to execute the updates.
Conversely:
- Code run on a background
Thread
must not update UI elements (or their properties). This is true both of JavaFX and Java Swing/AWT - Code run on a background thread may block or execute long-running tasks
Note that the java.util.Timer
and it's associated TimerTask
(which are part of the standard Java API) execute on a background thread created internally by the Timer
. This means that, although the API of Timer
and TimerTask
look somewhat similar to Timeline
, they must obey the rules of background threads (must not update the UI, etc).
Answered By - James_D