How to Use a Background Thread in Java

Prevent your application from freezing and making users feel frustrated

Joel Leblanc
6 min readJun 14, 2021

Software which runs on a single thread is rather rare these days. We’ll often create an application that consumes an API on the web and has a graphical user interface (GUI). If we handle user interaction, make API calls and update the GUI all on the same thread, our application will often appear to be “frozen”. That’s quite a pain. Thankfully, this issue can be solved using background threads.

Let’s take a look at the following example.

Your Uncle Harold wants to check his email, but he finds Outlook very confusing. He asks you, his favourite niece or nephew who studies computer science, if it’s possible to access his email with the click of a button. Since Harold is a nice guy, you decide to whip something up for him.

The requirements are crystal clear. Your client wants to see his email by clicking on a button. That means we need to create an application with a button and a list.

Here’s a mockup for our email client:

A mockup of a window that contains a table with a column for the sender, the subject, and the message, and a button to download the email.
Email Client Mockup

And now, let’s write some code.

To keep it simple, we’ll write the application in Java using the JavaFX library.

The Data

An email should have the following fields:

  • Sender
  • Receiver
  • Subject
  • Content

Since we’re making our email client just for Harold, we’ll omit the receiver field.

Here’s the POJO (Plain Old Java Object) we’ll use to represent an email:

public class Email {
private final String sender;
private final String subject;
private final String message;

public Email(String sender, String subject, String message) {
this.sender = sender;
this.subject = subject;
this.message = message;
}

public String getSender() {
return sender;
}

public String getSubject() {
return subject;
}

public String getMessage() {
return message;
}
}

The Email Client

We’ll fetch Harold’s email through an email client. For Harold, we’ll make a “fake” client which waits for a few seconds before responding with a preset email list.

public class EmailClient {
private final long delayBeforeResponseInMs;

public EmailClient(long delayBeforeResponseInMs) {
this.delayBeforeResponseInMs = delayBeforeResponseInMs;
}

public List<Email> fetchEmail() {
// Harold doesn't know it, but we won't really fetch email...
// We'll just pretend.
try {
Thread.sleep(delayBeforeResponseInMs);
} catch (InterruptedException error) {
System.err.println("Hmm... that didn't work");
}

return List.of(
new Email("Red Green", "The Handyman's Secret Weapon", "Duct tape can fix just about anything! If women don't find you handsome, they should at least find you handy."),
new Email("Winston Rothschild III", "Rothschild's Sewage and Septic Sucking Services", "We're number one in the number two business. We'll take that smell off your hands. We come in a truck and leave in a daze."),
new Email("Nigerian Prince", "Let Me Share a Fortune with You", "Hey! I want to share my money. Want some? Give me your bank account number, SIN, etc. and I'll send you a few million dollars!"));
}
}

Why? First of all, we don’t even know if Harold has an email address. Second, it makes it much easier to explain how to use a background thread.

I’m pretty confident that Harold will be thrilled to get some wise words from Red and read Winston’s company slogans. We’ll have to warn him about the advance-fee scam though…

The Graphical User Interface

We’ll use a table to display the email. It will have three columns, one for each field. Here is the method used to create the table:

private TableView<Email> createTable() {
TableColumn<Email, String> senderColumn = new TableColumn<>("Sender");
senderColumn.setCellValueFactory(dataFeatures -> new SimpleObjectProperty<>(dataFeatures.getValue().getSender()));

TableColumn<Email, String> subjectColumn = new TableColumn<>("Subject");
subjectColumn.setCellValueFactory(dataFeatures -> new SimpleObjectProperty<>(dataFeatures.getValue().getSubject()));

TableColumn<Email, String> messageColumn = new TableColumn<>("Message");
messageColumn.setCellValueFactory(dataFeatures -> new SimpleObjectProperty<>(dataFeatures.getValue().getMessage()));

TableView<Email> table = new TableView<>();
List.of(senderColumn, subjectColumn, messageColumn).forEach(column -> table.getColumns().add(column));

return table;
}

The table’s content comes from an observable list. When the list’s content changes, the table will be updated automatically.

ObservableList<Email> emailToDisplay = FXCollections.observableArrayList();

TableView<Email> theTable = createTable();
theTable.setItems(emailToDisplay);

We’ll use our email client to fetch the content which will be added to the list. The call to the client will be triggered when Harold clicks on the button.

Button theButton = new Button("Download Email");
theButton.setOnAction(event -> emailToDisplay.setAll(client.fetchEmail()));

When we put it all together, we get a simple email client application for Harold.

public class EmailClientForHarold extends Application {
private static final long DELAY_BEFORE_RESPONSE_IN_MS = 5000;

@Override
public void start(Stage primaryStage) {
EmailClient client = new EmailClient(DELAY_BEFORE_RESPONSE_IN_MS);

ObservableList<Email> emailToDisplay = FXCollections.observableArrayList();

TableView<Email> theTable = createTable();
theTable.setItems(emailToDisplay);
VBox.setVgrow(theTable, Priority.ALWAYS);

Button theButton = new Button("Download Email");
theButton.setOnAction(event -> emailToDisplay.setAll(client.fetchEmail()));

primaryStage.setScene(new Scene(new VBox(theTable, theButton)));
primaryStage.setTitle("Harold's Email Client");
primaryStage.show();
}
}

Let’s Take it for a Spin

The following window should appear when we run the application.

A window that contains a table with a column for the sender, subject, and message fields, and a button to download email.
Email Client

Not bad, eh? Click on the button.

An animated image of the application. The button to download emails is pressed, and the application freezes. Clicking on the table column headers does nothing. Once the preset three-second delay has elapsed, the application unfreezes and responds to user input.
The email client is frozen while it “downloads” the email

The application is completely frozen while the email client is downloading email from the server.

How can this be?

Picture a restaurant with a single employee. This one person takes orders, cooks the meals and serves them. Chances are that the customers will wait a long time before they get their food.

The restaurant with a single employee represents the application and its main thread, the JavaFX application thread.

The application has to react to the event raised when the button is clicked, fetch the email and redraw the GUI once the table’s content is updated. It can only do one thing at a time. That’s why the application appears to be frozen while it is fetching the email. It comes back to life once it is done.

Harold’s probably going to be upset if the application is frozen for too long.

We don’t want to make Harold upset.

How can we prevent freezing the application? By using a background thread to fetch the email.

The Antifreeze

Using CompletableFutures must be one of the most elegant and simplest ways to run a task on a background thread in Java.

Here is the button’s updated action which fetches email on a background thread and switches back to the main thread to update the table’s content.

theButton.setOnAction(event -> CompletableFuture.supplyAsync(client::fetchEmail)
.thenAcceptAsync(emailToDisplay::setAll, Platform::runLater));

Let’s break it down.

The static method, supplyAsync, tells the application that our client’s fetchEmail method will supply the email. It will call the method asynchronously, which means it will run it on a background thread.

The supplyAsync method returns a CompletableFuture. This object represents a value which will be available at some point in the future. Once the method call is done, the future is completed.

The future’s value, once completed, will be the email.

CompletableFuture<List<Email>> futureEmail = CompletableFuture.supplyAsync(client::fetchEmail);

It is impossible to know exactly when the email will be available, but we want to display them in the table once they are.

The CompletableFuture’s thenAcceptAsync method lets us do exactly what we want. It tells the application to take the email and put them in the list of email to display. The method accepts the result obtained from the previous method call in supplyAsync.

CompletableFuture.supplyAsync(client::fetchEmail)
.thenAcceptAsync(emailToDisplay::setAll, Platform::runLater);

The table is redrawn once the emailToDisplay list’s content is changed.

Each and every operation which impacts the GUI must be run on the JavaFX application thread. This explains the second parameter we passed to the thenAcceptAsync method, Platform::runLater. It tells the application to call the method on the application’s main thread.

Let’s run the application again. We should get a much better result.

An animated image of the application. The button to download email is pressed, but this time the application responds to clicks on the table column headers. Email appear in the table at the end, as expected.
The email client responds to user interactions while it “downloads” the email

Everything runs smoothly.

Our application now uses two threads. The main thread, the JavaFX application thread, handles user interaction and drawing the GUI to display the email while a background thread handles fetching them.

It’s a restaurant with two employees. The waiter takes orders and serves the meals once they are ready, while the cook prepares the food.

And there you have it!

We created an email client for Harold. If we ever create an email address for our dear uncle, we’ll be able to fetch email from a real server on the web without worrying about the GUI freezing up if he has a poor Internet connection.

The email client’s source code can be found in the following GitHub repository: https://github.com/leblancjs/email-client-for-harold.

--

--