Use ScheduledExecutorService to update JavaFX elements - java

Currently I am making a program that reminds me when to water my plants, while also putting the weather into account. I would like to display the current temperature and humidity, and I have made code that does that well enough already. However, this code only works when manually running the method via a button press, and throws Exception in thread "pool-3-thread-1" java.lang.IllegalStateException: Not on FX application thread; currentThread = pool-3-thread-1 when I attempt to run it in a ScheduledExecutorService. From my understanding JavaFX does not allow other threads to edit JavaFX components without Platform.runLater, however I can't seem to find anything about Platform.runLater being combined with ScheduledExecutorService.
Here is my update method:
public void update() {
final Runnable updater = new Runnable() {
public void run() {
humidityLabel.setText("Humidity: " + Double.toString(Weather.getHumidity()) + "%");
humidityDialArm.setRotate(Weather.getHumidity() * 1.8);
tempLabel.setText("Temperature: " + Double.toString(Weather.getTemperature()) + "°F");
temperatureDialArm.setRotate(Weather.getTemperature()*1.5);
icon = Weather.getIcon();
conditionLabel.setText(Weather.getCondition());
}
};
final ScheduledFuture<?> updaterHandle = scheduler.scheduleAtFixedRate(updater, 10, 10, TimeUnit.MINUTES);
}
And here is my main method:
public static void main(String[] args) {
App app = new App();
launch();
app.update();
}
I found a similar problem here, however I haven't been able to find a way to get Platform.runLater to work well with the ScheduledExecutorService. I also found this on GitHub, however I can't tell what the fix for this problem was other than it was fixable. I also tried putting a while loop at main that would just constantly update it, but that just caused the program to hang and eventually crash. Even if it did work, that would also make it not runnable for long periods of time as the API I am using limits the amount of GET requests per day.

Use ScheduledService
The javafx.concurrent.ScheduledService class provides a way to repeatedly do an action and easily communicate with the FX thread. Here is an example:
import javafx.concurrent.ScheduledService;
import javafx.concurrent.Task;
import javafx.scene.image.Image;
public class WeatherService extends ScheduledService<WeatherService.Result> {
#Override protected Task<Result> createTask() {
return new Task<>() {
#Override protected Result call() throws Exception {
// this is invoked on the background thread
return new Result(
Weather.getTemperature(),
Weather.getHumidity(),
Weather.getCondition(),
Weather.getIcon()
);
}
};
}
public record Result(double temperature, double humidity, String condition, Image icon) {}
}
Then where you use the service you'd add an on-succeeded handler to handle updating the UI:
service.setOnSucceeded(e -> {
var result = service.getValue();
// update UI (this handler is invoked on the UI thread)
});
To have it execute every 10 minutes, with an initial delay of 10 minutes, to match what you're doing with the ScheduledExecutorService, you would do:
service.setDelay(javafx.util.Duration.minutes(10));
service.setPeriod(javafx.util.Duration.minutes(10));
// you can define the thread pool used with 'service.setExecutor(theExecutor)'
When first configuring the service. You also need to maintain a strong reference to the service; if it gets garbage collected, then the task will not be rescheduled.
Use Platform#runLater(Runnable)
If you have to use ScheduledExecutorService for some reason, then you should run the code that updates the UI in a runLater call. Here's an example:
public void update() {
final Runnable updater =
() -> {
// get information on background thread
double humidity = Weather.getHumidity();
double temperature = Weather.getTemperature();
Image icon = Weather.getIcon();
String condition = Weather.getCondition();
// update UI on FX thread
Platform.runLater(
() -> {
humidityLabel.setText("Humidity: " + humidity + "%");
humidityDialArm.setRotate(humidity * 1.8);
tempLabel.setText("Temperature: " + temperature + "°F");
temperatureDialArm.setRotate(temperature * 1.5);
iconView.setImage(icon);
conditionLabel.setText(condition);
});
};
scheduler.scheduleAtFixedRate(updater, 10, 10, TimeUnit.MINUTES);
}

Related

Spontaneous execution of code in periodically running thread

I have a thread which executes code periodically, e. g. every 10 seconds. I'd like to have the option to also invoke the same code in a spontaneous way and not have to wait 10 seconds. But the code of the automatic and spontaneous execution must never run concurrently, instead they should run in sequence if the user presses the execute button while the thread is invoking the same method.
Does anyone know a good pattern or even a class that can address this kind of requirement?
First thing that comes to mind would be to make the work method synchronized. But in that case the manual execution (e. g. button press) is blocked and has to wait until the method in the thread is finished. Is there a better approach without blocking?
Example:
public class Executor extends Thread {
// endless loop, executes work method periodically with pause inbetween
#Override
public void run() {
while( true) {
work( "automatic");
pause(10000);
}
}
// Working method that's executed periodically or manually
private synchronized void work( String text) {
System.out.println( "Working " + text + " " + System.currentTimeMillis());
}
// helper method that pauses the thread
private static void pause( long sleepMs) {
try {
Thread.sleep(sleepMs);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
// start automatic execution
Executor executor = new Executor();
executor.start();
// pause a while
pause(1000);
// manual execution
executor.work( "manual");
}
}
Edit: Solution for my requirement:
public class ScheduledExecutor {
public static void main(String[] args) throws InterruptedException {
ScheduledThreadPoolExecutor executor = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(1);
executor.scheduleWithFixedDelay(new Work("auto"), 0, 10, TimeUnit.SECONDS);
Thread.sleep(1000);
executor.execute(new Work("manual"));
}
public static class Work implements Runnable {
String text;
public Work(String text) {
this.text = text;
}
#Override
public void run() {
System.out.println("Working " + text + " " + System.currentTimeMillis());
}
}
}
I would create a new, single-thread executor service:
ExecutorService executorService = Executors.newFixedThreadPool(1);
Then, I would set up a timer that feeds the executorService a task once every 10 seconds.
new Timer(10000, new ActionListener {
public void actionPerformed(ActionEvent evt) {
executorService.execute(() -> doWhatever());
}
}).start();
Finally, you can call executorService.execute(() -> doWhatever()); in your button press handler, or wherever else you want in your code.
Only one activation of doWhatever() will run at a time because the executorService has only one thread on which to run them. And, your button press handler will never have to wait, because it does nothing but put a new object on a queue.
I have a thread which executes code periodically, e. g. every 10 seconds. I'd like to have the option to also invoke the same code in a spontaneous way and not have to wait 10 seconds.
A simple way to do this in your code is not to pause by using Thread.sleep(...) but rather do wait(...). Then whenever you want the command to wakeup and run manually it just does a notify().
So you code would look something like:
while( true) {
work( "automatic");
synchronized (this) {
try {
// wait for a bit but allow someone else to awake us to run manually
wait(10000);
} catch (InterruptedException ie) {
// always a good pattern
Thread.currentThread().interrupt();
return;
}
}
}
Then when you want to have it run manually you do:
synchronized (executor) {
executor.notify();
}
The notify will awaken the thread immediately so that it can run it's task. The work method then does not need to be synchronized because only the Executor thread is running it.
NOTE: As pointed out by #shinobi, using wait() like this could suffer from spurious wake-ups which can happen with certain OS thread implementations.
Lastly, it is a better practice to make Executor implement Runnable as opposed to extending Thread.
Share a semaphore between the server thread (the one that executes the task) and client threads (the ones that need to trigger immediate execution):
Semaphore sem = new Semaphore( 0 );
The server thread needs to execute the following code (note that it's an endless loop — you'll likely want to plug-in your program termination check as the condition to while()):
while( true ) {
try {
sem.tryAcquire( 10, TimeUnit.SECONDS );
} catch( InterruptedException e ) {
continue;
}
runTask();
sem.drainPermits();
}
Then, in order to trigger immediate execution, the client thread needs to do:
sem.release();
Thus, the server thread will execute the task upon either acquiring a permit from the semaphore as soon as a client thread releases one (triggered immediate execution,) or timing-out in Semaphore.tryAcquire() (periodic executions 10s apart, end-to-start.) Having executions 10s apart start-to-start will take some slightly more involved logic, as well as keeping track of last execution's starting time, but the basic idea remains the same.
You need to drain the permits each time in order to avoid multiple back-to-back executions of the task, in cases where it might be triggered for immediate execution while still being executed.

JavaFX splash screen message and progress not updating

I have a JavaFX application that checks for the presence of a database at startup. If the database is not found, a "seed" database must be created before the program can proceed.
Since creating the seed database can be lengthy, I want to display a splash screen and show progress updates as the process proceeds. Only after the seed database is completely written should the program proceed.
Here is the class for the splash screen:
public final class SplashWindow {
private final Label message;
private final ProgressBar progress;
private final Stage splashStage;
public SplashWindow() {
Image img = new Image(IMAGE_PREFIX + "splash_image.png");
double imgWidth = img.getWidth();
ImageView splashImage = new ImageView(img);
splashImage.setFitWidth(imgWidth);
splashImage.setPreserveRatio(true);
message = new Label("Saving seed database...");
message.setPrefWidth(imgWidth);
message.setAlignment(Pos.CENTER);
progress = new ProgressBar();
progress.setPrefWidth(imgWidth);
Pane splashVBox = new VBox(3);
splashVBox.setPadding(new Insets(5));
splashVBox.getChildren().addAll(splashImage, progress, message);
splashStage = new Stage(StageStyle.UTILITY);
splashStage.setScene(new Scene(splashVBox));
}
public void bindMessageProperty(ReadOnlyStringProperty sp) {
message.textProperty().bind(sp);
}
public void bindProgressProperty(ReadOnlyDoubleProperty dp) {
progress.progressProperty().bind(dp);
}
public void show() {
splashStage.show();
}
public void shutdown() {
message.textProperty().unbind();
progress.progressProperty().unbind();
splashStage.hide();
}
}
When run, the splash screen shows correctly with the image, progress bar and text message area.
The SplashWindow class is called by the following method:
private void saveSeedDatabase(ObservableList<RefModel> docList) {
SplashWindow splash = new SplashWindow();
Task<Integer> saveTask = new Task<Integer>() {
#Override
protected Integer call() throws InterruptedException {
updateMessage("Saving references...");
int docsSaved = 0;
for (RefModel rm : docList) {
if (isCancelled()) {
updateMessage("Cancelled");
break;
}
updateMessage("Saving: " + rm.getTitle());
saveNewReference(rm);
docsSaved++;
updateProgress(docsSaved, docList.size());
}
updateMessage("Saved " + docsSaved + " references to database");
return docsSaved;
}
};
saveTask.setOnSucceeded((WorkerStateEvent t) -> {
splash.shutdown();
});
splash.bindMessageProperty(saveTask.messageProperty());
splash.bindProgressProperty(saveTask.progressProperty());
splash.show();
new Thread(saveTask).start();
try {
saveTask.get();
} catch (InterruptedException | ExecutionException ex) {
// Do nothing
}
}
When run, the splash screen is displayed but never shows the update messages and progress. It shows a wait cursor when the mouse if over the splash screen.
If the try/catch block at the end of the method is commented out, the main program attempts to proceed without waiting for the database to be written. In this case, the splash screen is hidden by the main program window, but does display the update messages as it works. From debugging this, it looks like everything is running on the correct thread -- the database stuff is on a worker thread, the SplashWindow stuff is on the FX event thread.
It seems clear that the call to saveTask.get() is blocking the UI thread, but I am not sure why.
#JewelSea has written a nice alternative that doesn't fit my program architecture very well. It would not be impossible to alter my program to work with his solution.
However, I don't understand why the get() call blocks the UI. What am I doing wrong.
According to the JavaDocs:
(get()) Waits if necessary for the computation to complete, and then retrieves its result.
Retrieving the computed value without blocking the GUI Thread would be done with getValue(). However: This method only returns a result, when the Task has successfully finished its work. That is why you should do this aysnc in the onSucceeded block.

safe publication with final local variable object reference

Would the following Java code be thread-safe, or does it suffer from possible visibility/safe publication problems?
import java.util.ArrayList;
import java.util.List;
public class WouldThisBeSafe {
public static void main(String[] args) throws InterruptedException {
final List<String> result = new ArrayList<>();
Runnable job = new Runnable() {
#Override
public void run() {
result.add("Hello");
result.add(" ");
result.add("world!");
}
};
Thread t = new Thread(job);
t.start();
t.join();
System.out.println("result = " + result);
}
}
In my real application I have a semi-long running task that needs to load data from a server using multiple remote method calls and must run in the foreground blocking the UI, while reporting progress in a dialog. For this I am using Eclipse/JFace's ProgressMonitorDialog together with an anonymous IRunnableWithProgress instead of Runnable to report the progress. The progress monitor runs the task in a background thread (fork) and shows the progress dialog until the thread is done.
Of course my real question would be whether my actual code with ProgressMonitorDialog and an anonymous IRunnableWithProgress inner class is thread safe, but I suspect that it is equivalent to the simplified example above.
Since you call
t.join();
immediately after
t.start();
the code is perfectly thread-safe. The only issue could have been if you created multiple threads that access result, or if you had tried to print result before joining.

JavaFX 2 StringProperty does not update field until enclosing method returns

I would like to update a Label in a JavaFX application so that the text changes multiple times as the method runs:
private void analyze(){
labelString.setValue("Analyzing"); // (labelString is bound to the Label during initialization)
// <Some time consuming task here>
labelString.setValue("Analysis complete!");
}
But when I run this, the label does not update until the task finishes, and just displays whatever it was before until the analyze() method returns.
How can I force update the label so that it will show "Analyzing" in the beginning followed by "Analysis complete!" when the task is complete?
Assuming you are invoking your analyze() method on the FX Application Thread (e.g. in an event handler), your time consuming code is blocking that thread and preventing the UI from updating until it is complete. As #glen3b says in the comments, you need to use an external thread to manage this code.
JavaFX provides a Task API which helps you do this. In particular, it provides methods which invoke code on the Java FX Application thread for you, allowing you to update the UI safely from your background Task.
So you can do something like
private void analyze() {
Task<Void> task = new Task<Void>() {
public Void call() {
updateMessage("Analyzing");
// time consuming task here
updateMessage("Analysis complete");
}
};
labelString.bind(task.messageProperty());
new Thread(task).start();
}
If you need to unbind the StringProperty when the task is complete, you can do
task.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent event) {
labelString.unbind();
}
});

Platform.runLater and Task in JavaFX

I have been doing some research on this but I am still VERY confused to say the least.
Can anyone give me a concrete example of when to use Task and when to use Platform.runLater(Runnable);? What exactly is the difference? Is there a golden rule to when to use any of these?
Also correct me if I'm wrong but aren't these two "Objects" a way of creating another thread inside the main thread in a GUI (used for updating the GUI)?
Use Platform.runLater(...) for quick and simple operations and Task for complex and big operations .
Use case for Platform.runLater(...)
Use case for Task: Task Example in Ensemble App
Example: Why Can't we use Platform.runLater(...) for long calculations (Taken from below reference).
Problem: Background thread which just counts from 0 to 1 million and update progress bar in UI.
Code using Platform.runLater(...):
final ProgressBar bar = new ProgressBar();
new Thread(new Runnable() {
#Override public void run() {
for (int i = 1; i <= 1000000; i++) {
final int counter = i;
Platform.runLater(new Runnable() {
#Override public void run() {
bar.setProgress(counter / 1000000.0);
}
});
}
}).start();
This is a hideous hunk of code, a crime against nature (and
programming in general). First, you’ll lose brain cells just looking
at this double nesting of Runnables. Second, it is going to swamp the
event queue with little Runnables — a million of them in fact.
Clearly, we needed some API to make it easier to write background
workers which then communicate back with the UI.
Code using Task :
Task task = new Task<Void>() {
#Override public Void call() {
static final int max = 1000000;
for (int i = 1; i <= max; i++) {
updateProgress(i, max);
}
return null;
}
};
ProgressBar bar = new ProgressBar();
bar.progressProperty().bind(task.progressProperty());
new Thread(task).start();
it suffers from none of the flaws exhibited in the previous code
Reference :
Worker Threading in JavaFX 2.0
Platform.runLater: If you need to update a GUI component from a non-GUI thread, you can use that to put your update in a queue and it will be handled by the GUI thread as soon as possible.
Task implements the Worker interface which is used when you need to run a long task outside the GUI thread (to avoid freezing your application) but still need to interact with the GUI at some stage.
If you are familiar with Swing, the former is equivalent to SwingUtilities.invokeLater and the latter to the concept of SwingWorker.
The javadoc of Task gives many examples which should clarify how they can be used. You can also refer to the tutorial on concurrency.
It can now be changed to lambda version
#Override
public void actionPerformed(ActionEvent e) {
Platform.runLater(() -> {
try {
//an event with a button maybe
System.out.println("button is clicked");
} catch (IOException | COSVisitorException ex) {
Exceptions.printStackTrace(ex);
}
});
}
One reason to use an explicite Platform.runLater() could be that you bound a property in the ui to a service (result) property. So if you update the bound service property, you have to do this via runLater():
In UI thread also known as the JavaFX Application thread:
...
listView.itemsProperty().bind(myListService.resultProperty());
...
in Service implementation (background worker):
...
Platform.runLater(() -> result.add("Element " + finalI));
...

Categories

Resources