0

I have a JavaFX app that runs two threads at startup. One is the UI thread that must not be blocked. The other is a thread that prepares a large table (it takes about 20 seconds). I want to signal the UI thread when the second thread is done, so it can change the color of a rectangle from red to green. I have tried solutions using the synchronized keyword, but they all caused the UI thread to be blocked.

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
  • 1
    No need for `synchronization` here probably. once the non-gui thread is finished, callback the function should mark that red to green, but make sure that function is called by event-dispatcher –  Sep 20 '20 at 14:48
  • [`Platform.runLater(...)`](https://docs.oracle.com/javase/8/javafx/api/javafx/application/Platform.html#runLater-java.lang.Runnable-)? – Turing85 Sep 20 '20 at 14:48
  • You can find your answer here: https://stackoverflow.com/questions/14221617/waiting-for-thread-while-updating-swing – Mihai Pasca Sep 20 '20 at 14:51
  • @MihaiPasca the answer you provided a link to relates to _Swing_ while the question states that JavaFX is being used. Am I missing something? – Abra Sep 20 '20 at 15:15
  • Perhaps this Web page will help? [Concurrency in JavaFX](https://docs.oracle.com/javase/8/javafx/interoperability-tutorial/concurrency.htm) – Abra Sep 20 '20 at 15:18

2 Answers2

1

I used the following resources to obtain the below code.

The below app simply displays a red rectangle which, after five seconds, turns to green. Explanations after the code.

import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.concurrent.Worker;
import javafx.concurrent.Worker.State;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class JfxTask0 extends Application {
    private Task<Void>  task;

    @Override
    public void init() throws Exception {
        task = new Task<Void>() {
            
            @Override
            protected Void call() throws Exception {
                try {
                    Thread.sleep(5000L);
                }
                catch (InterruptedException xInterrupted) {
                    if (isCancelled()) {
                        System.out.println("CANCELLED!");
                    }
                }
                return null;
            }
        };
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        Rectangle rect = new Rectangle(25.0d, 25.0d, 50.0d, 50.0d);
        rect.setFill(Color.RED);
        task.stateProperty().addListener(new ChangeListener<Worker.State>() {

            @Override
            public void changed(ObservableValue<? extends State> workerStateProperty,
                                Worker.State oldValue,
                                Worker.State newValue) {
                if (newValue == Worker.State.SUCCEEDED) {
                    rect.setFill(Color.GREEN);
                }
            }
        });
        new Thread(task).start();
        Group root = new Group();
        ObservableList<Node> children = root.getChildren();
        children.add(rect);
        Scene scene = new Scene(root, 100.0D, 100.0D);
        primaryStage.setScene(scene);
        primaryStage.setTitle("Task");
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

Method init() is declared in class javafx.application.Application. It is executed before method start() and, as its name suggests, is used to initialize the JavaFX application. In this method I create the background task. The background task merely sleeps for five seconds.

In method start() I create the red rectangle and then launch the background task but before launching the task, I register a listener with one of the task's properties. This property will be set to a particular value once the task completes.

After the task is launched, I build the rest of the GUI and display it.

Once the task terminates, then listener is invoked and it sets the rectangle color to green.

Abra
  • 19,142
  • 7
  • 29
  • 41
0

You can use a handler for this problem. there is example Add this in your main activity and create handler.

Handler h = new Handler(){
@Override public void handleMessage(Message msg){
switch(msg.what){
case 1:
    // what you want when complete 
    break;
default:
break;
}
}

}

MyThread thread = new MyThread(new Messenger(h));
thread.start();

Now add this in your thread file.

public class MyThread{
Messenger m;
public MyThread(Messenger m){
this.m = m;
}

public void run(){
super.run();

// your codes

//
//when your task complete 
Message msg = Message.obtain();
msg.what = 1;
msg.obj = "";
try{
m.send(msg);
}catch(IOException e){
e.printStackTrace();
}
}

}

Dharman
  • 30,962
  • 25
  • 85
  • 135