Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

java thread immediately update UI

Tags:

java

javafx

I have a javaFX application which visualizes compuational geometry algorithms. The execution of an algorithm happens in another thread, lets call it mainComputingThread. An algorithm can update the UI at any time by adding/removing/modifying shapes. so the code will look like:

//do some computaions (1)
updateUI();
//do some more calculations (2)

What I want know is in the updateUI method to update the UI immediately and prevent the calling thread from running further (marked as (2)) until the UI update is done.

I thought about boolean guards. So the code could would look like:

updateUI(){
   boolean guard = false;
   Platform.runLater(new Runnable()
   {
      run(){
        //do the actual update
        guard = true;
      }
   });
   while(guard==false);
}

I hope you get an idea of what I mean. I'm really curious if there's a better solution for this problem...

like image 275
Manuel Jain Avatar asked Jun 01 '15 09:06

Manuel Jain


People also ask

Can we update UI from thread?

Worker threads However, note that you cannot update the UI from any thread other than the UI thread or the "main" thread. To fix this problem, Android offers several ways to access the UI thread from other threads. Here is a list of methods that can help: Activity.

Which thread can update the user interface?

Only the Main thread, also known as the UI thread can update the UI a.f.a.i.k.

Why we update UI on main thread ios?

Making it thread-safe wouldn't buy you much in terms of performance;it would in fact make many things slower. And the fact that UIKit is tied to the main thread makes it very easy to write concurrent programs and use UIKit. All you have to do is make sure that calls into UIKit are always made on the main thread.

How threads are executed in Java?

start. Causes this thread to begin execution; the Java Virtual Machine calls the run method of this thread. The result is that two threads are running concurrently: the current thread (which returns from the call to the start method) and the other thread (which executes its run method).


3 Answers

Simple approach: block background thread until update is complete:

You need to update the UI on the FX Application Thread. Typically you do this by passing a plain Runnable to Platform.runLater(...).

If you want to wait for that ui update to complete before proceeding, instead create a FutureTask and pass it to Platform.runLater(...). Then you can call get() on the FutureTask, which will block until the task is complete:

private void updateUI() throws InterruptedException {

    // actual work to update UI:
    FutureTask<Void> updateUITask = new FutureTask(() -> {

        // code to update UI...

    }, /* return value from task: */ null);

    // submit for execution on FX Application Thread:
    Platform.runLater(updateUITask);

    // block until work complete:
    updateUITask.get();
}

This lets the FutureTask handle all the tricky work of waiting and notifying: it is always better to use a higher-level API for this kind of work when you can.

If you like, you can refactor this into a utility method, similarly to Dainesch's answer:

public class FXUtils {

    public static void runAndWait(Runnable run) throws InterruptedException {
        FutureTask<Void> task = new FutureTask<>(run, null);
        Platform.runLater(task);
        task.get();
    }
}

Alternative approach: ensure that no more than one update is consumed during any frame rendering, blocking the background thread if an update is pending

Here is a somewhat different approach. Create a BlockingQueue with a capacity of 1 to hold the Runnables that update the UI. From your background thread, submit the Runnables to the blocking queue: since the blocking queue can hold at most one element, this will block if one is already pending.

To actually execute the updates in the queue (and remove them, so more can be added), use an AnimationTimer. This looks like:

private final BlockingQueue<Runnable> updateQueue = new ArrayBlockingQueue<>(1);

background thread code:

// do some computations...

// this will block while there are other updates pending:    
updateQueue.put(() -> {
    // code to update UI
    // note this does not need to be explicitly executed on the FX application
    // thread (no Platform.runLater()). The animation timer will take care of that
});

// do some more computations

Create the timer to consume the updates:

AnimationTimer updateTimer = new AnimationTimer() {

    @Override
    public void handle(long timestamp) {
        Runnable update = updateQueue.poll();
        if (update != null) {
            // note we are already on the FX Application Thread:
            update.run();
        }
    }
};

updateTimer.start();

This basically ensures that no more than one update is ever scheduled at any time, with the background thread blocking until any pending updates are consumed. The animation timer checks (without blocking) for pending updates on each frame rendering, ensuring that every update is executed. The nice thing about this approach is that you can increase the size of the blocking queue, effectively keeping a buffer of pending updates, while still ensuring no more than one update is consumed during any single frame rendering. This might be useful if there are occasional computations that take longer than others; it gives these computations a chance to be calculated while others are waiting to be executed.

like image 160
James_D Avatar answered Sep 24 '22 18:09

James_D


Hard question to answer without having the reason why you want to stop processing before the UI update is done. (Note: the runLater method executes the UI updates in the order received) Is it to prevent spamming to many Runnables to the JavaFX thread? Other reasons?

Your basic idea however works with the use of a CountDownLatch so that the processing thread waits to acquire a permit. If you choose that approach use something like this:

public class MyFXUtils {

    public static runAndWait(final Runnable run) {
        final CountDownLatch doneLatch = new CountDownLatch(1);
        Platform.runLater(new Runnable() {
            public void run() {
                 try {
                     run.run();
                 } finally {
                     doneLatch.countDown();
                 }
            }
        });
        doneLatch.await();
    }
}

EDIT: replaced Semaphore by CountDownLatch

like image 32
Dainesch Avatar answered Sep 23 '22 18:09

Dainesch


EDIT:

So, the quickest way I always do it in prototypes is as following, transform:

//do some computaions (1)
updateUI();
//do some more calculations (2)

into

ExecutorService executor = Executors.newFixedThreadPool(1);
class JobStep implements Runnable {

  public void run() {
     doSomeComputations();
     Platform.runLater(() -> {
        updateUI();
        executor.submit(new JobStep());
     });
  }
executor.submit(new JobStep());

OLD PART

Not an answer, but a suggestion how to attack the problem.

From my experience, the complete solution would be much more elaborate. I would separate the JavaFX shape instances from the shapes, which your algorithm does process. I would do it by means of using different class types and synchronize between the two.

The graphical algorithms have the tendency to be a lot quicker than the ones that are visualizing it. If the algorithm runs on small data set, then the rendering most often tend to slow down it significantly. It can easily be seen by running the same algorithm with and without visualization.

If the data set is bigger than the most trivial ones, then drawing of a single frame can easily take more than one second. Interactive visualizations are expected to respond in "real time", preferably many times per second.

The data visualization facilities have many means to tackle the problem. The best candidates always include:

  1. Simplifying visualization. Drawing simpler shapes instead of complex, like removing the rounding from boxes. The LOD (level of detail) also applies to this point: during interactive scroll the visualized elements might be replaced by bounding box counterparts.

  2. Selective hiding. Drawing only a part of the whole data set.

  3. Parallelizing and hardware acceleration. The GPU's natively provide many means to handle complex visualizations. Typically the low level programming APIs (OpenGL, shader programs) allow much better throughput than every high level wrapping API, including JavaFX

Most often, the end solutions incorporate not only above points, but also others, including domain specific optimizations.

The visualization facilities always come with a lot of restrictions, like the most common one: have to be updated in the dedicated thread (thread confinement approach). They also come with visualization specific data structures.

From the data processing algorithm stage, one of the most common requirements is that it cannot be blocked or delayed by visualization. The algorithms are also written in a style, which doesn't translate to well for the means of visualization: imperative loops over data structures instead of updating observable drawable objects. There is a good reason to it though: the algorithms are expected to be optimized for performance or memory consumption.

One architectural approach to the problem might be as following:

  1. The data processing stage produces snapshots at predefined points. The adding, modifying and remove operations are all published as this packet. It can be a just a copy of data structure that is being processed or it can be in the form of the coalesced events.

  2. The data processing and data visualization run on different threads. They communicate only by means of publishing snapshots, never by blocking each other directly.

  3. The snapshots shouldn't be restricted to particular frame rate. There should be means to batch updates before drawing or drawing same batch multiple times if the data processing stage stalls.

I strongly recommend reactive approach to the problem. The RxJava provides nice example for the "suggestion box" feature. It's very good at correctly handling requirements like "Every key update do a long running process on different thread and discard the last one, if any was running. Or maybe, don't do it on every key update, but wait 50ms for the user to make up his mind before he ends typing".

like image 30
Rekin Avatar answered Sep 26 '22 18:09

Rekin