Как защелка не влияет на 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 обычно означает, что вы можете избежать загрязнения рук защелками, замками и т. Д.