java thread立即更新UI

Man*_*ain 5 java javafx

我有一个javaFX应用程序可视化计算几何算法.算法的执行发生在另一个线程中,让我们调用它mainComputingThread.算法可以通过添加/删除/修改形状随时更新UI.所以代码看起来像:

//do some computaions (1)
updateUI();
//do some more calculations (2)
Run Code Online (Sandbox Code Playgroud)

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

我想到了布尔警卫.所以代码可能看起来像:

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

我希望你能明白我的意思.我真的好奇是否有更好的解决方案来解决这个问题...

Jam*_*s_D 6

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

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

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

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();
}
Run Code Online (Sandbox Code Playgroud)

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

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

public class FXUtils {

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

替代方法:确保在任何帧渲染期间不会消耗多于一个更新,如果更新处于挂起状态,则阻止后台线程

这是一种有点不同的方法.创建一个BlockingQueue容量1来容纳Runnable更新UI 的s.从后台线程中,将Runnables 提交到阻塞队列:由于阻塞队列最多只能容纳一个元素,因此如果一个元素已经挂起,这将阻止.

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

private final BlockingQueue<Runnable> updateQueue = new ArrayBlockingQueue<>(1);
Run Code Online (Sandbox Code Playgroud)

后台线程代码:

// 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
Run Code Online (Sandbox Code Playgroud)

创建计时器以使用更新:

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();
Run Code Online (Sandbox Code Playgroud)

这基本上确保了在任何时候都不会安排多于一个更新,后台线程会阻塞,直到消耗任何挂起的更新.动画计时器检查(不阻止)每个帧渲染的挂起更新,确保每次更新都执行.这种方法的好处在于,您可以增加阻塞队列的大小,有效地保留挂起的更新缓冲区,同时仍确保在任何单帧渲染期间不会消耗多于一个更新.如果偶尔计算花费的时间比其他计算时间长,这可能会有用; 它为这些计算提供了计算的机会,而其他计算则等待执行.