Multithreading JavaFX管理绑定到场景生命周期的轮询线程
在下面的示例中,有一个FXML控制器运行一个单独的线程来定期更新组件 如果用户单击导航离开此控制器并返回此控制器,则将有2个线程在后台运行。(第一个从未停止过) 如何确保只有一个线程,并且该线程与控制器的生命周期相关联Multithreading JavaFX管理绑定到场景生命周期的轮询线程,multithreading,javafx,Multithreading,Javafx,在下面的示例中,有一个FXML控制器运行一个单独的线程来定期更新组件 如果用户单击导航离开此控制器并返回此控制器,则将有2个线程在后台运行。(第一个从未停止过) 如何确保只有一个线程,并且该线程与控制器的生命周期相关联 <BorderPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="example.BorderController" fx:id="rootPane">
<BorderPane xmlns:fx="http://javafx.com/fxml/1"
fx:controller="example.BorderController"
fx:id="rootPane">
<left>
<VBox>
<Button text="Pane1" onAction"#goToPane1"/>
<Button text=Pane2" onAction="goToPane2"/>
</VBox>
</left>
</BorderPane>
在两个窗格之间导航后,输出如下所示:
Pane1 example.Pane1@45892b4c
Pane1 example.Pane1@6dc7392e
Pane1 example.Pane1@45892b4c
Pane1 example.Pane1@6dc7392e
// Repeats
您可以在窗格中观察节点的
sceneProperty()
,并相应地启动或关闭ScheduledExecutor
:
import java.net.URL;
import java.time.Instant;
import java.util.ResourceBundle;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.layout.Pane;
public class Pane1Controller implements Initializable {
@FXML
private Label toUpdate;
@FXML
private Pane root;
private ScheduledExecutorService scheduler;
@Override
public void initialize(URL url, ResourceBundle rb) {
root.sceneProperty().addListener((obs, oldScene, newScene) -> {
if (newScene == null && scheduler != null) {
scheduler.shutdown();
}
if (newScene != null) {
scheduler = Executors.newScheduledThreadPool(1, runnable -> {
Thread t = new Thread(runnable);
t.setDaemon(true);
return t ;
});
scheduler.scheduleAtFixedRate(() -> {
Platform.runLater(() -> {
System.out.println("Pane1 " + this);
toUpdate.setText(Instant.now().toString());
});
} , 2, 2, TimeUnit.SECONDS);
}
});
}
}
窗格中的任何节点通常都可以工作,不过使用窗格本身(我在这里给出了fx:id=“root”
)可能最有意义。注意,我还将执行器使用的线程设置为守护进程线程,因此它不会阻止应用程序退出
最后,请注意,对于示例中使用的功能(其中计划任务中的所有内容都在FX应用程序线程上执行),动画API可能比java.util.concurrent
API更干净,尽管实际应用程序可能需要后者:
import java.net.URL;
import java.time.Instant;
import java.util.ResourceBundle;
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.layout.Pane;
import javafx.util.Duration;
public class Pane1Controller implements Initializable {
@FXML
private Label toUpdate;
@FXML
private Pane root;
private Timeline timeline ;
@Override
public void initialize(URL url, ResourceBundle rb) {
timeline = new Timeline(new KeyFrame(Duration.seconds(2), e-> {
System.out.println("Pane1 " + this);
toUpdate.setText(Instant.now().toString());
}));
timeline.setCycleCount(Animation.INDEFINITE);
root.sceneProperty().addListener((obs, oldScene, newScene) -> {
if (newScene == null) {
timeline.pause();
} else {
timeline.play();
}
});
}
}
import java.net.URL;
import java.time.Instant;
import java.util.ResourceBundle;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.layout.Pane;
public class Pane1Controller implements Initializable {
@FXML
private Label toUpdate;
@FXML
private Pane root;
private ScheduledExecutorService scheduler;
@Override
public void initialize(URL url, ResourceBundle rb) {
root.sceneProperty().addListener((obs, oldScene, newScene) -> {
if (newScene == null && scheduler != null) {
scheduler.shutdown();
}
if (newScene != null) {
scheduler = Executors.newScheduledThreadPool(1, runnable -> {
Thread t = new Thread(runnable);
t.setDaemon(true);
return t ;
});
scheduler.scheduleAtFixedRate(() -> {
Platform.runLater(() -> {
System.out.println("Pane1 " + this);
toUpdate.setText(Instant.now().toString());
});
} , 2, 2, TimeUnit.SECONDS);
}
});
}
}
import java.net.URL;
import java.time.Instant;
import java.util.ResourceBundle;
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.layout.Pane;
import javafx.util.Duration;
public class Pane1Controller implements Initializable {
@FXML
private Label toUpdate;
@FXML
private Pane root;
private Timeline timeline ;
@Override
public void initialize(URL url, ResourceBundle rb) {
timeline = new Timeline(new KeyFrame(Duration.seconds(2), e-> {
System.out.println("Pane1 " + this);
toUpdate.setText(Instant.now().toString());
}));
timeline.setCycleCount(Animation.INDEFINITE);
root.sceneProperty().addListener((obs, oldScene, newScene) -> {
if (newScene == null) {
timeline.pause();
} else {
timeline.play();
}
});
}
}