visibility of data during(/after) thread execution
concurrency, java, javafx
Solution
Is it guaranteed that the worker thread sees the values of the Input instance?
Yes: `Thread#start` creates a happens before-relationship. So you have:
- `input.setName("Jack");` happens-before `workerThread.start();` because they both are on the FX Thread so you get the guarantees of program order
- workerThread.start(); happens-before `result.setName(input.getName());` for the reason you mentionned (all actions before a thread is started happen before any actions executed in that thread)
Is it guaranteed that the JavaFX Application Thread sees the values of the Result instance?
Yes, `Platform.runLater` also provides a happens-before relationship as hinted by the javadoc: "This method [...] may be called from any thread". If you are not convinced you can have a look at the code and you will see that there are a few synchronization points.
Without that guarantee it would be impossible to communicate with JavaFX components without manually synchronizing everything.
Problem
In this example there is an instance of an Input class and a Result class. The input instance is filled in a GUI-Thread, here the JavaFX Application Thread. After pressing the button this input instance is used in a worker thread. Then the worker thread creates an instance of the Result class and fills it with some values after this Platform.runLater is invoked to update the GUI. My first question is: Is it guaranteed that the worker thread sees the values of the Input instance? I would say yes, because of the following: JLS 17.4.5. Happens-before Order, says: A call to start() on a thread happens-before any actions in the started thread. So from my point of view everything done in the JavaFX Thread before start is invoked will be visible to the worker thread. My second question is: Is it guaranteed that the JavaFX Application Thread sees the values of the Result instance? I think yes, but i am not sure. Will Platform.runLater ensure this? How? ``` package javafxconcurrency; import javafx.application.Application; import javafx.application.Platform; import javafx.concurrent.Task; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.layout.StackPane; import javafx.stage.Stage; public class JavaFxConcurrency extends Application { private Input input; @Override public void start(Stage primaryStage) { input = new Input(); input.setId(1); input.setName("Jack"); Button btn = new Button(); btn.setText("Start a thread"); btn.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { Task<Result> task = new Task() { @Override protected Object call() throws Exception { final Result result = queryDB(input); Platform.runLater(new Runnable() { @Override public void run() { updateGUI(result); } }); return result; } private Result queryDB(Input input) { try { Thread.sleep(3000); Result result = new Result(); result.setId(System.currentTimeMillis()); result.setName(input.getName()); return result; } catch (InterruptedException ex) { throw new RuntimeException(ex); } } }; Thread workerThread = new Thread(task); workerThread.setDaemon(true); workerThread.start(); } }); StackPane root = new StackPane(); root.getChildren().add(btn); Scene scene = new Scene(root, 300, 250); primaryStage.setTitle("Visibilty"); primaryStage.setScene(scene); primaryStage.show(); } private void updateGUI(Result result) { System.out.println("result" + result); } public static void main(String[] args) { launch(args); } private static class Input { private long id; private String name; public void setId(long id) { this.id = id; } public long getId() { return id; } public void setName(String name) { this.name = name; } public String getName() { return name; } } private static class Result { private long id; private String name; public void setId(long id) { this.id = id; } public long getId() { return id; } public void setName(String name) { this.name = name; } public String getName() { return name; } @Override public String toString() { return "Result{" + "id=" + id + ", name=" + name + '}'; } } } ```