latch如何在javafx中没有效果?

时间:2017-05-10 17:43:32

标签: javafx countdownlatch

我在开发javafx时遇到问题,我发现latch对JavaFx没有影响,例如,在以下代码中:

public class JavafxLatchDemo1 extends Application {

    @Override
    public void start(Stage primaryStage) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(1);
        TextArea txtOut = new TextArea();

        StackPane root = new StackPane();
        root.getChildren().add(txtOut);

        Scene scene = new Scene(root, 300, 250);

        //invoke rpc function
        Callable<Integer> fibCall = new fibCallable(latch, txtOut);
        FutureTask<Integer> fibTask = new FutureTask<Integer>(fibCall);
        Thread fibThread = new Thread(fibTask);
        fibThread.start();
        latch.await(); //阻塞等待计数为0 


        txtOut.appendText("\n Say 'Hello World'");
        primaryStage.setTitle("Hello World!");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    } 
}
class fibCallable implements Callable<Integer>{        
    CountDownLatch latch;
    TextArea txtInput;

    fibCallable(CountDownLatch mylatch, TextArea txtIn){
        latch = mylatch;
        txtInput = txtIn;
    }

    @Override
    public Integer call() throws Exception {
        int temp1=1,temp2=0;

        System.out.println("Client will pay money for eshop");

        for(int i=0; i<10; i++){
            temp1 = temp1 + temp2;
            temp2 = temp1;
        }
        System.out.println("Client already decide to pay money for eshop");

        Platform.runLater(()->{
            txtInput.appendText("\nWhy, am I first?");

        });
        latch.countDown(); //计数减1 

        return (new Integer(temp1));
    }  
}

因为我设置了一个锁存器来阻止latch.await();中的JavaFx主线程,并希望可调用线程fibCallable首先输出内容:所以我希望:

Why, am I first?
 Say 'Hello World'

但实际输出相反:

Say 'Hello World'
Why, am I first?

为什么呢?和解决方案?

1 个答案:

答案 0 :(得分:2)

Platform.runLater()提交要在FX应用程序线程上执行的runnable。 start()方法也在FX应用程序线程上执行。因此,使用Runnable提交的Platform.runLater()在该线程上已经执行的任何内容完成之前无法执行。

所以你在后台启动你的fibThread,然后立即等待锁存:这会阻止FX应用程序线程。 fibThread做了一些工作,然后提交了对(阻止的)FX应用程序线程的调用。然后fibThread释放锁存器:当前阻止的FX应用程序线程解除阻塞并完成当前方法调用(将文本"Say Hello World"附加到文本区域并显示该阶段),并在此之后的某个时刻提交给Platform.runLater()的runnable在同一个线程上执行。

“快速而肮脏”的修复只是将第二次调用包裹在另一个txtOut.appendText(...)的{​​{1}}中:

Platform.runLater()

这保证可以正常工作,因为传递给Platform.runLater(() -> txtOut.appendText("\n Say 'Hello World'")); 的runnables保证按照它们传递的顺序执行,倒计时锁存器在两次调用{{}之间建立“before-before”关系{1}}。

但请注意,您通过调用Platform.runLater()来阻止FX应用程序线程,这是不好的做法(并且会延迟显示阶段直到后台线程完成)。你应该把调用Platform.runLater()和第二个latch.await()放在另一个后台线程中。另请注意,您根本不需要锁存器,因为您已经有latch.await(),并且您可以等待其结果(这相当于等待锁存器)。所以你可以做到

Platform.runLater()

最后,请注意JavaFX有自己的concurrency API,它直接支持FX应用程序线程上的各种回调。这个API通常意味着你可以避免用闩锁和锁等弄脏手。