How can I run something in one thread and wait for the result in a different thread using Java/JavaFX?

TL;DR

The code is as simple as:

import java.util.concurrent.CountDownLatch;

final CountDownLatch latchToWaitForJavaFx = new CountDownLatch(1);

Platform.runLater(() -> {
  ...REMEMBER THROWS IN THIS THREAD, WILL NOT IMPACT THE CALLING THREAD...
  latchToWaitForJavaFx.countDown();
});

latchToWaitForJavaFx.await();

Details…

Hopefully you would rarely need to synchronize two threads. This is normally an indicator of a poor design – ideally each thread would be able to operate independently. However, I have found a common need to synchronize between threads when testing JavaFX GUIs using JUnit.

For instance, you may want to invoke some GUI behavior, and then check if the GUI was updated. For these cases, you will need to find a way to block the testing thread until the GUI action has completed, this can be done using a CountDownLatch as follows:

import java.util.concurrent.CountDownLatch;
...
@Test
public void showAnchorPane_shouldFocusTextField() throws Exception {
  // Note: We are in the testing thread.  

  // Here we create the count down latch which is thread safe:
  final CountDownLatch latchToWaitForJavaFx = new CountDownLatch(1);

  // We now create a runnable and register that runnable to be called
  // by the JavaFX thread using the Platform.runLater method. This call
  // is asynchronous and returns immediately:
  Platform.runLater(new Runnable() {
    @Override
    public void run() {
      // Do required work on JavaFX thread...
      Stage stage = new Stage();
      Scene scene = new Scene(anchorPane);
      stage.setScene(scene);
      stage.show();

// If we threw before this line, while in the JavaFX thread, then the
// calling thread would never know!

      // Now the work is done we release the testing thread:
      latchToWaitForJavaFx.countDown();
    }
  });

  // Because the last call returned immediately, we wait here until the
  // JavaFX thread has completed it's work for this test:
  latchToWaitForJavaFx.await();

  // Now check if GUI is open and that the right object has focus, etc.
}

Let’s refactor to a function:

private void runAndWaitOnJavaFx(Runnable guiWorker) throws Throwable {
  final CountDownLatch latchToWaitForJavaFx = new CountDownLatch(1);
  Platform.runLater(() -> {
    guiWorker.run();
// If we threw before this line, while in the JavaFX thread, then the
// calling thread never would know!
    latchToWaitForJavaFx.countDown();
  });
  latchToWaitForJavaFx.await();
}

To deal with the  strange behavior if the runnable were to throw and the parent thread wouldn’t know…

private void runAndWaitOnJavaFx(Runnable guiWorker) throws Throwable {
  final CountDownLatch latchToWaitForJavaFx = new CountDownLatch(1);
  final Throwable[] javaFxException = {null};
  Platform.runLater(() -> {
    try {
      guiWorker.run();
    } catch (Throwable e) {
      javaFxException[0] = e;
    } finally {
      latchToWaitForJavaFx.countDown();
    }
  });
  latchToWaitForJavaFx.await();
  if (javaFxException[0] != null) {
    throw javaFxException[0];
  }
}
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s