java thread立即更新UI [英] java thread immediately update UI

查看:226
本文介绍了java thread立即更新UI的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个javaFX应用程序可视化计算几何算法。算法的执行发生在另一个线程中,我们称之为 mainComputingThread
算法可以通过添加/删除/修改形状随时更新UI。
所以代码如下:

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)

我想知道的是在 updateUI 立即更新UI并阻止调用线程进一步运行(标记为(2))直到UI更新完成的方法。

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...

推荐答案

简单方法:阻止后台线程直到更新完成:

您需要更新FX应用程序线程上的UI。通常,您可以通过将简单的 Runnable 传递给 Platform.runLater(...)来执行此操作。

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

如果你想在继续之前等待ui更新完成,而是创建一个 FutureTask 并将其传递给平台.runLater(...)。然后你可以在 FutureTask 上调用 get(),这将阻塞直到任务完成:

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();
}

这让 FutureTask 处理所有等待和通知的棘手工作:在可能的情况下,最好使用更高级别的API进行此类工作。

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.

如果您愿意,您可以将此重构为实用程序方法,类似于Dainesch的答案:

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();
    }
}

替代方法:确保不超过在任何帧渲染期间都会消耗一个更新,如果更新未决,则阻止后台线程

这是一种稍微不同的方法。创建 BlockingQueue ,容量 1 以保存 Runnable s更新UI。从后台线程中,将 Runnable 提交给阻塞队列:由于阻塞队列最多只能容纳一个元素,如果一个元素已经挂起,这将阻止。

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.

要实际执行队列中的更新(并删除它们,可以添加更多),请使用 AnimationTimer 。这看起来像:

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);

后台线程代码:

// 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.

这篇关于java thread立即更新UI的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆