There's a nice article about this here: https://docs.oracle.com/javafx/2/threads/jfxpub-threads.htm
The highlights are the that "Java FX" way are through the use of a Task and a Service.
You create a Task that has several housekeeping methods for life cycle and such, and you submit it to a Service for execution. The Service will work with a Thread or and Executor Service.
You want to use a Task because it's FX specific. As it says in the documentation:
Because the Task is designed for use with JavaFX GUI applications, it
ensures that every change to its public properties, as well as change
notifications for state, errors, and for event handlers, all occur on
the main JavaFX application thread.
From the article and the documentation, here is a simple example:
public static class FirstLineService extends Service<String> {
private StringProperty url = new SimpleStringProperty(this, "url");
public final void setUrl(String value) { url.set(value); }
public final String getUrl() { return url.get(); }
public final StringProperty urlProperty() { return url; }
protected Task createTask() {
final String _url = getUrl();
return new Task<String>() {
protected String call() throws Exception {
URL u = new URL(_url);
BufferedReader in = new BufferedReader(
new InputStreamReader(u.openStream()));
String result = in.readLine();
in.close();
return result;
}
};
}
}
Once create, you invoke it with:
service.start();