首页 文章

停止主线程,直到JavaFX事件队列中的所有事件都已执行

提问于
浏览
0

在调试应用程序时,我希望主线程在每个Runnable之后等待我使用的JavaFX事件队列

Platform.runLater(new Runnable()... )

等到它被执行(即可见) . 但是这里有两个曲折:

首先,它不是一个标准的,GUI驱动的JavaFX应用程序 . 它是一个显示和更新JavaFX阶段的脚本 . 所以结构看起来像这样:

public static void main(String [] args){
    //do some calculations
    SomeView someView = new SomeView(data); //SomeView is basically a wrapper for  a stage
    PlotUtils.plotView(someView) //displays SomeView (i.e. the stage)
    //do some more calculations
    someView.updateView(updatedData)
    //do some more calculations
}

public class SomeView {
    private static boolean viewUpdated = false;
    private ObservableList<....> observableData;
    public void updateView(Data data){
        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                observableData.addAll(data);
                boolean viewUpdated = true; 
            }
        });
    //If configured (e.g using boolean switch), wait here until
    //the Runnable has been executed and the Stage has been updated.
    //At the moment I am doing this by waiting until viewUpdated has been
    //set to true ... but I am looking for a better solution!
    }
}

其次,应该很容易禁用这个“功能”,即等待Runnable被执行(使用当前的方法这是没有问题的,但也可以使用替代方法) .

做这个的最好方式是什么?

例如 . 有什么像阻塞版本在JavaFX线程上执行Runnable或有一个简单的方法来检查事件队列上的所有事件是否已执行/事件队列是否为空....?

2 回答

  • 1

    这是基于JavaFX2: Can I pause a background Task / Service?的总体思路

    基本的想法是提交 FutureTask<Void>Platform.runLater() ,然后在 FutureTask 上调用 get() . get() 将阻止,直到任务完成:

    // on some background thread:
    
    Runnable runnable = () -> { /* code to execute on FX Application Thread */};
    FutureTask<Void> task = new FutureTask<>(runnable, null);
    Platform.runLater(task);
    task.get();
    

    must not 在FX应用程序线程上执行此代码块,因为这将导致死锁 .

    如果您希望这可以轻松配置,您可以执行以下操作:

    // Wraps an executor and pauses the current thread 
    // until the execution of the runnable provided to execute() is complete
    
    // Caution! Calling the execute() method on this executor from the same thread
    // used by the underlying executor will result in deadlock.
    
    public class DebugExecutor implements Executor {
        private final Executor exec ;
    
        public DebugExecutor(Executor executor) {
            this.exec = executor ;
        }
    
        @Override
        public void execute(Runnable command) {
            FutureTask<Void> task = new FutureTask<>(command, null);
            exec.execute(command);
            try {
                task.get();
            } catch (InterruptedException interrupt) {
                throw new Error("Unexpected interruption");
            } catch (ExecutionException exc) {
                throw new RuntimeException(exc);
            }
        }
    }
    

    现在在您的应用程序中,您可以:

    // for debug:
    Executor frontExec = new DebugExecutor(Platform::runLater);
    // for production:
    // Executor frontExec = Platform::runLater ;
    

    并用 frontExec.execute(...); 替换所有对 Platform.runLater(...) 的调用

    根据您想要的配置方式,您可以根据命令行参数或属性文件有条件地创建 frontExec (或者,如果您使用依赖注入框架,则可以注入它) .

  • 1

    还有 PlatformImpl.runAndWait() 使用倒计时锁存器,只要你不从JavaFX线程调用它

相关问题