更改根节点时后台线程未关闭[javaFX]
我正在创建一个javafx程序,该程序的导航菜单通过更改场景的根来工作。根都继承自Pane类。一些窗格具有运行的后台线程。但是,当事件处理程序更改根窗格时,该窗格会切换,但后台线程不会停止。这会导致线程读取NFC时出现问题,并导致多个线程尝试读取NFC读取器。更改根节点时后台线程未关闭[javaFX],java,multithreading,javafx,nfc,Java,Multithreading,Javafx,Nfc,我正在创建一个javafx程序,该程序的导航菜单通过更改场景的根来工作。根都继承自Pane类。一些窗格具有运行的后台线程。但是,当事件处理程序更改根窗格时,该窗格会切换,但后台线程不会停止。这会导致线程读取NFC时出现问题,并导致多个线程尝试读取NFC读取器。 如何关闭后台线程?(从创建线程的窗格外部)或者我是否需要以不同的方式设置线程。 (线程设置为守护进程)。 线程在窗格构造函数中创建,如下所示: (我假设,当切换窗格时,它们属于该窗格,线程将停止。事实并非如此) 我以静态方式切换窗格,如下
如何关闭后台线程?(从创建线程的窗格外部)或者我是否需要以不同的方式设置线程。 (线程设置为守护进程)。 线程在窗格构造函数中创建,如下所示: (我假设,当切换窗格时,它们属于该窗格,线程将停止。事实并非如此) 我以静态方式切换窗格,如下所示:(此方法位于自己的类中)
您应该添加一种方法,将侦听器注册到负责替换场景根的类中。这允许您收到此类更改的通知,并通过终止线程作出反应 示例
@FunctionalInterface
public interface NodeReplaceListener {
public void onNodeReplace();
}
顺便说一句:请注意,对场景的修改不应来自任何线程,而应来自应用程序线程。使用
Platform.runLater
从不同线程进行更新:
Platform.runLater(() -> signIn.setText("Scan your card to sign in/out"));
正如你所说:
一些窗格具有运行的后台线程
我假设您在自己的窗格
实现中创建这些线程。我将向您展示一个包含抽象类的解决方案,您的每个窗格(或者至少是作为根插入的窗格)都应该扩展该抽象类。如果你没有,我强烈建议你这样做。但是,您没有提供关于这些窗格的太多信息,因此我将把我的答案集成到代码中的任务留给您(如果您决定遵循它的话)
稍后我将返回到volatile
关键字
现在您可以在activateThread
方法中创建线程,最好是在OwnPane
或其子类中,例如:
public void activateNFCThread(){
Runnable r = new Runnable(){
@Override
public void run () {
while(isRoot){
// what the thread has to do ...
}
}
};
Thread nfcCheckThread = new Thread(r);
nfcCheckThread.setDaemon(true);
nfcCheckThread.start();
}
现在我可以解释为什么必须使用volatile
关键字:字段isRoot
将由不同的线程使用。使用volatile
关键字,我们确保所有线程都将访问相同的“变量”(否则,出于性能原因,每个线程将有自己的版本)。线程是在
OwnPane
(或子类)中的方法中创建的,允许从Runnable
对象中访问isRoot
字段。在OwnPane
的子类中,甚至可以重写setAsRoot
方法,以便在调用setAsRoot
方法时直接启动NFS线程(如果需要):
最后,您可以使用以下方法更改舞台中场景的根窗格:
// All your methods regarding stage changes are static, so I'll leave this one static too
public static void changeRoot(Stage stage, OwnPane newRoot){
OwnPane oldStage = (OwnPane)stage.getScene().getRoot();
oldStage.unsetAsRoot();
Platform.runLater(() -> { //Platform.runLater to be sure the main thread that will execute this
//(only main thread is allowed to change something in the JavaFX nodes)
stage.getScene().setRoot(newRoot);
newRoot.setAsRoot();
});
}
当jvm退出或main返回时,守护进程线程退出。如果要提前退出,则需要手动关闭它们。执行此操作的标准方法是让回退线程定期检查“keepGoing”标志,您可以在希望它们退出时取消设置该标志。作为deamon线程的线程除了不阻止运行时关闭之外没有任何效果。您需要实现一些逻辑来自己关闭这些线程。。。可能您可以使用一些从侦听器更新到
场景
根目录的原子引用
,它允许线程
s检查场景的根目录是否仍然是“它们的”根目录,并以其他方式终止,但是很难推荐任何内容,因为关于您的设计的信息量很小。。。顺便说一句,使用executor服务可能会有所帮助。如果您没有在代码中显示线程是如何创建和关闭的,我们无法确定您是否需要以不同的方式设置线程!请提供一些重新创建您的情况的方法。它应该检查什么以确定它是否应该继续运行我尝试使用:if(getScene().getRoot().isDisable());但是它不起作用。你在哪里定义你的第一个代码样本被调用??HomePane是自制的吗??
private Parent root;
private NodeReplaceListener listener;
private Scene scene;
public void setRoot(Parent root, NodeReplaceListener listener) {
if (root != this.root && this.listener != null) {
this.listener.onNodeReplace();
}
this.root = root;
this.listener = listener;
scene.setRoot(root);
}
@Override
public void start(Stage primaryStage) {
Button btn = new Button("Next Scene");
btn.setOnAction((ActionEvent event) -> {
// replace root
setRoot(new StackPane(new Rectangle(100, 100)), null);
});
StackPane root = new StackPane();
root.getChildren().add(btn);
scene = new Scene(new Group(), 100, 100);
class MyRunnable implements Runnable {
// running volatile to guarantee that visibility of written values to all threads
volatile boolean running = true;
int i;
@Override
public void run() {
while (running) {
System.out.println(i++);
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
}
}
}
public void cancel() {
running = false;
}
}
MyRunnable r = new MyRunnable();
Thread t = new Thread(r);
t.setDaemon(true);
t.start();
primaryStage.setScene(scene);
// cancel runnable when root is replaced
setRoot(root, r::cancel);
primaryStage.show();
}
Platform.runLater(() -> signIn.setText("Scan your card to sign in/out"));
public abstract class OwnPane extends Pane {
protected volatile boolean isRoot = false;
public void setAsRoot(){
isRoot = true;
}
public void unsetAsRoot(){
isRoot = false;
}
}
public void activateNFCThread(){
Runnable r = new Runnable(){
@Override
public void run () {
while(isRoot){
// what the thread has to do ...
}
}
};
Thread nfcCheckThread = new Thread(r);
nfcCheckThread.setDaemon(true);
nfcCheckThread.start();
}
public class PaneWithNFCReader extends OwnPane {
@Override
public void setAsRoot(){
super.setAsRoot();
activateNFCThread();
}
}
// All your methods regarding stage changes are static, so I'll leave this one static too
public static void changeRoot(Stage stage, OwnPane newRoot){
OwnPane oldStage = (OwnPane)stage.getScene().getRoot();
oldStage.unsetAsRoot();
Platform.runLater(() -> { //Platform.runLater to be sure the main thread that will execute this
//(only main thread is allowed to change something in the JavaFX nodes)
stage.getScene().setRoot(newRoot);
newRoot.setAsRoot();
});
}