-
Notifications
You must be signed in to change notification settings - Fork 0
Why does my GUI freeze?
When an event happens, like a button click event, a long-running task may need to be performed
- Processing large data sets - large collections or databases
- Performing operations that block - typically IO operations
- IO operations - reading files or loading network data
If the long-running task is executed on the UI thread, your program may freeze.
Event listener methods run on a UI Thread
- Event Dispatch Thread for Swing
- FX Application Thread for JavaFX
Your events will be placed in a queue. If you block up the queue, other events such as rendering will have to wait.
The following methods can determine if your code is ran on a UI thread
-
Swing:
SwingUtilities.isEventDispatchThread()- returns
trueif on the Event Dispatch Thread (EDT)
- returns
public void actionPerformed(ActionEvent e) {
System.out.println(SwingUtilities.isEventDispatchThread()); // true
}-
JavaFX:
Platform.isFxApplicationThread()- returns
trueif on FX Application Thread
- returns
public void handle(ActionEvent event) {
System.out.println(Platform.isFxApplicationThread()); // true
}There are many ways to create threads. See Thread Objects and High Level Concurrency Objects (more specifically Executors)
// could be Swing methods like actionPerformed
// could be JavaFX methods handle
// could be your own custom method - see Diagnosing to find out
public void yourMethod() {
Thread thread = new Thread(() -> {
// long-running task
});
thread.start();
}If your long-running task updates the UI, make sure those updates are on the UI thread.
- Example Code
public void yourMethod() {
Thread thread = new Thread(() -> {
for(long l = 0; l < Long.MAX_VALUE; l++)
if(l % 31 == 0)
// update UI to display value of l
});
thread.start();
}- Swing Solution
for(long l = 0; l < Long.MAX_VALUE; l++) {
if(l % 31 == 0) {
long displayValue = l; // variable must be effectively final
SwingUtilities.invokeLater(() -> {
// update UI to display value
});
}
}~ invokeLater will put your code at the end of the queue
~ invokeAndWait will do the same, but will block the current thread until the queued code is executed
- JavaFX Solution
for(long l = 0; l < Long.MAX_VALUE; l++) {
if(l % 31 == 0) {
long displayValue = l; // variable must be effectively final
Platform.runLater(() -> {
// update UI to display value
});
}
}~ runLater will put your code at the end of the queue
~ There is no runAndWait - you must implement this yourself
Introducing a new thread helps, but a new problem arises: we are putting too many events into the EDT's queue
This will only work if you don't update the UI frequently
If your long-running code updates the UI frequently, you should use a worker
public void actionPerformed(ActionEvent e) {
SwingWorker<Void, Long> worker = new SwingWorker() {
protected Void doInBackground() {
for(long l = 0; l < Long.MAX_VALUE; l++)
if(l % 31 == 0)
publish(l);
return null;
}
protected void process(List<Long> items) {
Long latest = items.get(items.size() - 1);
// display latest to text area
}
}
}WARNING: If using primitives, autoboxing could cause performance overhead. Use the system which best suits your program's needs.
Task
TODO
Service
TODO
- fx/appthread/FXApplicationThreadBlockDemo
- fx/appthread/FXApplicationThreadDemo
- fx/worker/TaskDemo
- TODO - Create & include code example for
ServiceDemo