Как защелка не влияет на javafx?

Я сталкиваюсь с проблемой при разработке 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));
    }  
}

Так как я установил защелку, чтобы остановить основной поток JavaFx в latch.await();и хочу вызываемую нить fibCallable сначала выведите контент, так что я ожидаю:

Why, am I first?
 Say 'Hello World'

но реальный результат противоположен:

Say 'Hello World'
Why, am I first?

Зачем? и решение?

1 ответ

Platform.runLater() отправляет исполняемый файл для выполнения в потоке приложения FX. start() метод также выполняется в потоке приложений FX. Итак Runnable Вы отправили с Platform.runLater() не может быть выполнено, пока что-либо уже выполняющееся в этом потоке не завершится.

Итак, вы начинаете fibThread в фоновом режиме, а затем сразу дождитесь защелки: это блокирует поток приложения FX. fibThread выполняет небольшую работу, а затем отправляет вызов в (заблокированную) ветку приложения FX. Тогда fibThread освобождает фиксатор: заблокированный в данный момент поток приложения FX разблокируется и завершает текущий вызов метода (добавляя текст "Say Hello World" к текстовой области и отображению сцены), и в какой-то момент после этого Platform.runLater() выполняется в том же потоке.

"Быстро и грязно" исправить это просто обернуть второй вызов txtOut.appendText(...) в другой Platform.runLater():

Platform.runLater(() -> txtOut.appendText("\n Say 'Hello World'"));

Это гарантированно сработает, потому что runnables переданы Platform.runLater() гарантированно выполняются в том порядке, в котором они были переданы, а защелка обратного отсчета устанавливает отношение "происходит до" между двумя вызовами Platform.runLater(),

Однако обратите внимание, что вы блокируете поток приложения FX с помощью вызова latch.await(), что является плохой практикой (и задержит отображение сцены, пока не завершится фоновый поток). Вы должны действительно позвонить latch.await()вместе со вторым Platform.runLater() в другой фоновой ветке. Также обратите внимание, что вам на самом деле не нужна защелка вообще, так как у вас уже есть FutureTaskи вы можете просто дождаться его результата (это будет эквивалентно ожиданию защелки). Так что вы можете сделать

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

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);
        Callable<Integer> fibCall = new fibCallable(txtOut);
        FutureTask<Integer> fibTask = new FutureTask<Integer>(fibCall);
        Thread fibThread = new Thread(fibTask);
        fibThread.start();
//        latch.await(); //阻塞等待计数为0 


        new Thread(() -> {
            try {
                // wait for fibTask to complete:
                fibTask.get();
                // and now append text to text area,
                // but this now must be done back on the FX Application Thread
                Platform.runLater(() -> txtOut.appendText("\n Say 'Hello World'"));
            } catch (Exception ignored) {
                // ignore interruption: thread is exiting anyway....
            }
        }).start();
        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(TextArea txtIn){
            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));
        }  
    }
}

Наконец, обратите внимание, что JavaFX имеет собственный API параллелизма, который напрямую поддерживает различные обратные вызовы в потоке приложений FX. Этот API обычно означает, что вы можете избежать загрязнения рук защелками, замками и т. Д.

Другие вопросы по тегам