6

I am calling a method inside my UI thread. Inside this method a new thread is created. I need the UI thread to wait until this new thread is finished because I need the results of this thread to continue the method in the UI thread. But I don´t want to have UI frozen while its waiting. Is there some way to make the UI thread wait without busy waiting?.

Jupiter
  • 91
  • 1
  • 1
  • 7
  • 1
    If the UI thread waits, the UI is frozen: the two phrases mean exactly the same thing. Why do you want the UI thread to "wait"? Can you explain more what you are actually trying to achieve? – James_D Apr 22 '15 at 14:22
  • The application I am working on is not set up for multithreading, so the UI thread is always doing all the work. There is a class that acts as a bottleneck for all the calls to the server and I need to add multithreading to that class. I have no access to the objects calling this bottleneck class however. – Jupiter Apr 22 '15 at 20:37
  • Sorry, that makes no sense. What it wrong with just using the approach I show in the answer? – James_D Apr 22 '15 at 20:42
  • I wouldn't be able to return to the call stack that triggered the creation of the Thread. What I want is the UI thread to continue the code in the method that creates the Thread once the Thread finishes. – Jupiter Apr 22 '15 at 20:53
  • Why though? This is the wrong solution to whatever problem you are trying to solve. If you block the UI thread, then the UI necessarily becomes unresponsive. What is it you are actually trying to do that makes you think you need to do this? – James_D Apr 22 '15 at 21:23
  • I need to do this because I have no access to the logic that updates the UI from the class that acts as bottleneck. Unfortunately I cannot modify the code that updates the UI to allow me to call it once the process is done, thus I have to return the results from the call stack. – Jupiter Apr 22 '15 at 21:32
  • No you don't. The solution I posted covers all of this. See updated answer, in which I spell that out. (Why don't you just try it...?) – James_D Apr 22 '15 at 21:38
  • Perhaps you can post some (pseudo?)code to make it clearer what you would actually like to do? – James_D Apr 23 '15 at 16:24

2 Answers2

12

You should never make the FX Application Thread wait; it will freeze the UI and make it unresponsive, both in terms of processing user action and in terms of rendering anything to the screen.

If you are looking to update the UI when the long running process has completed, use the javafx.concurrent.Task API. E.g.

someButton.setOnAction( event -> {

    Task<SomeKindOfResult> task = new Task<SomeKindOfResult>() {
        @Override
        public SomeKindOfResult call() {
            // process long-running computation, data retrieval, etc...

            SomeKindOfResult result = ... ; // result of computation
            return result ;
        }
    };

    task.setOnSucceeded(e -> {
        SomeKindOfResult result = task.getValue();
        // update UI with result
    });

    new Thread(task).start();
});

Obviously replace SomeKindOfResult with whatever data type represents the result of your long-running process.

Note that the code in the onSucceeded block:

  1. is necessarily executed once the task has finished
  2. has access to the result of the execution of the background task, via task.getValue()
  3. is essentially in the same scope as the place where you launched the task, so it has access to all the UI elements, etc.

Hence this solution can do anything you could do by "waiting for the task to finish", but doesn't block the UI thread in the meantime.

Ollie
  • 1,641
  • 1
  • 13
  • 31
James_D
  • 201,275
  • 16
  • 291
  • 322
  • This would be indeed ideal, if I could write this code in a class where the UI elements are in the same scope as the call to the bottleneck class. Unfortunately this is not the case. I cannot add code to the classes handling the calls to the method in the bottleneck class. – Jupiter Apr 23 '15 at 15:50
1

Just call a method that notifies the GUI when the Thread is finished. Something like this:

class GUI{

   public void buttonPressed(){
      new MyThread().start();
   }

   public void notifyGui(){
      //thread has finished!

      //update the GUI on the Application Thread
      Platform.runLater(updateGuiRunnable)
   }

   class MyThread extends Thread{
      public void run(){
         //long-running task

         notifyGui();
      }
   }
}
Kevin Workman
  • 41,537
  • 9
  • 68
  • 107
  • But note that with this code, `notifyGui()` will be called from a background thread, so it can't update the UI directly: any UI updates must be scheduled on the FX Application Thread. And doing that, any data you use will be set in one thread and accessed from another, so you need to be (a little) careful to make sure you access live values. Assuming you want to update the UI, it's much easier to use the `javafx.concurrent` API. – James_D Apr 22 '15 at 14:33
  • @James_D I know, and I figured that was separate from the actual question. I've edited my answer to include the basics of updating the UI on the Application Thread. – Kevin Workman Apr 22 '15 at 14:42
  • Yeah, hard to know as the OP doesn't explain why (s)he wants the FX Application Thread to "wait"... Question falls into [this category](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). – James_D Apr 22 '15 at 14:45
  • @James_D True enough. Reminds me of [this](http://www.catb.org/esr/faqs/smart-questions.html#goal) as well. – Kevin Workman Apr 22 '15 at 14:47
  • I see. If I implement this, however, I wouldn't be able to achieve what I want. Which is returning to the call stack that triggered the creation of the Thread, with the results. – Jupiter Apr 22 '15 at 20:43
  • @Jupiter Well, no. You can't have it both ways. You either have to wait for the thread to finish, or you have the thread signal you when its done. – Kevin Workman Apr 22 '15 at 21:34
  • There's no earthly reason for you want to return to the exact same place in the call stack. As long as you have access to the results of the long-running process, you have all you need. – James_D Apr 22 '15 at 21:52
  • @Kevin Workman I see, thank you. Is there some way, however, to make busy waiting perform some other task while it waits? instead of checking constantly if the task is done can it also perform something else in intervals of probing for termination and performing something else? – Jupiter Apr 23 '15 at 15:53