JavaFX properties are not thread-safe. Memory consistency is not guaranteed: Java is allowed to create a copy of the memory containing the property object for the background thread at any time. When this happens, any changes to the property happening later won't be visible to the background thread.
It's not too hard to make sure the value is accessible from the thread though. Depending on the frequency of the access and the delay you're willing accept for the information retrieval, the following approaches could work for you:
Updating a AtomicReference
from a listener
This way you simply make sure the updates become visible to the background thread by assigning the value to a AtomicReference
on the application thread:
final AtomicReference<Duration> time = new AtomicReference<>(player.getCurrentTime());
player.currentTimeProperty().addListener((o, oldValue, newValue) -> time.set(newValue));
Thread t = new Thread(() -> {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
}
System.out.println(time.get());
}
});
t.setDaemon(true);
t.start();
The drawback is that the updates to the reference happen more often than necessary. (A volatile
field may do the trick too btw.)
Querying the property using Platform.runLater
As alternative you could simply schedule a runnable reading the variable using Platform.runLater
. This approach does not require a reference to be continuously updated:
Thread t = new Thread(() -> {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
}
CompletableFuture<Duration> future = new CompletableFuture<>();
Platform.runLater(() -> future.complete(player.getCurrentTime()));
try {
Duration time = future.get(); // get value as soon as evaluated on application thread
System.out.println(time);
} catch (InterruptedException e) {
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
}
});
t.setDaemon(true);
t.start();
Note: for both approaches you need to deal with the fact that you may set the player
field to null
. Note that any test is subject to the same memory consistency issues as described at the start of the answer. The first approach would require you to make the field volatile
to make sure the change is visible to the background thread too, for the second approach you could either cancel the future or throw cause an exception to notify the caller: future.completeExceptionally
and future.cancel(true)
result in future.get()
yielding an ExecutionException
or a CancelationException
respecively.