新线程,阶段关闭后应用程序仍然 运行
new Thread, application still running after Stage-close
所以我遵循了这个教程:https://www.youtube.com/watch?v=gyyj57O0FVI
我在 javafx8 中编写了完全相同的代码。
public class CountdownController implements Initializable{
@FXML
private Label labTime;
@Override
public void initialize(URL location, ResourceBundle resources) {
new Thread(){
public void run(){
while(true){
Calendar calendar = new GregorianCalendar();
int hour = calendar.get(Calendar.HOUR);
int minute = calendar.get(Calendar.MINUTE);
int second = calendar.get(Calendar.SECOND);
String time = hour + ":" + minute + ":" + second;
labTime.setText(time);
}
}
}.start();
}
我关闭Window后,application/thread在系统中仍然是运行。我猜这是因为无限循环,但线程不应该随着应用程序关闭而终止吗?
第二件事是,当我尝试为 Label 设置文本时出现错误:
Exception in thread "Thread-4" java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-4
at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:204)
at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:364)
at javafx.scene.Parent.onProposedChange(Parent.java:364)
at com.sun.javafx.collections.VetoableListDecorator.setAll(VetoableListDecorator.java:113)
at com.sun.javafx.collections.VetoableListDecorator.setAll(VetoableListDecorator.java:108)
at com.sun.javafx.scene.control.skin.LabeledSkinBase.updateChildren(LabeledSkinBase.java:575)
at com.sun.javafx.scene.control.skin.LabeledSkinBase.handleControlPropertyChanged(LabeledSkinBase.java:204)
at com.sun.javafx.scene.control.skin.LabelSkin.handleControlPropertyChanged(LabelSkin.java:49)
at com.sun.javafx.scene.control.skin.BehaviorSkinBase.lambda$registerChangeListener(BehaviorSkinBase.java:197)
at com.sun.javafx.scene.control.skin.BehaviorSkinBase$$Lambda4/1099655841.call(Unknown Source)
at com.sun.javafx.scene.control.MultiplePropertyChangeListenerHandler.changed(MultiplePropertyChangeListenerHandler.java:55)
at javafx.beans.value.WeakChangeListener.changed(WeakChangeListener.java:89)
at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:182)
at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81)
at javafx.beans.property.StringPropertyBase.fireValueChangedEvent(StringPropertyBase.java:103)
at javafx.beans.property.StringPropertyBase.markInvalid(StringPropertyBase.java:110)
at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:143)
at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:49)
at javafx.beans.property.StringProperty.setValue(StringProperty.java:65)
at javafx.scene.control.Labeled.setText(Labeled.java:146)
at application.CountdownController.run(CountdownController.java:29)
...是的,我将阅读更多关于线程的内容,但我想知道这些问题的答案。
第一部分
线程在创建时独立于其他线程运行。你有一个无限循环的新线程,这意味着它会永远保持 运行,即使在舞台关闭后也是如此。
通常情况下,不建议使用死循环,因为跳出死循环非常困难。
建议您使用:
然后您可以调用其中之一(根据您使用的是什么)
当你的舞台关闭时。您可以使用类似 :
stage.setOnCloseRequest(closeEvent -> {
timertask.cancel();
});
JavaFX API的 (感谢James_D的评论)
这些不需要显式取消,因为 ScheduledService
使用守护线程并且 AnimationTimer
在 JavaFX 线程上运行。
第二部分
你的问题的第二部分已经在论坛中多次回答了。
You need to be on the JavaFX Application thread to use scene graph elements.
由于您创建了一个新线程并尝试更新 label
,这是一个 JavaFX 节点,它抛出了异常。更多信息,请访问:
JavaFX error when trying to remove shape
Why am I getting java.lang.IllegalStateException "Not on FX application thread" on JavaFX?
Javafx Not on fx application thread when using timer
就我而言,使用 ScheduledExecutorService
你不能轻易将它设置为守护进程,我不想玩 stage.setOnCloseRequest(closeEvent -> {});
使用 AnimationTimer,我无法像您建议的那样在迭代之间执行 Thread.sleep(100)
之类的操作,因为 "AnimationTimer runs on the JavaFX thread."
ScheduledService
现在对我来说很难理解...
所以,当我阅读和阅读它时,我得出结论,也许这个简单的选择是最好的:
public class CountdownController implements Initializable{
@FXML
private Label labTime;
@FXML
private Button buttSTOP;
@Override
public void initialize(URL location, ResourceBundle resources) {
Timer timer = new Timer(true); //set it as a deamon
timer.schedule(new MyTimer(), 0, 1000);
}
public class MyTimer extends TimerTask{
@Override
public void run() {
Calendar calendar = new GregorianCalendar();
int hour = calendar.get(Calendar.HOUR);
int minute = calendar.get(Calendar.MINUTE);
int second = calendar.get(Calendar.SECOND);
String time = hour + ":" + minute + ":" + second;
Platform.runLater(() -> {
labTime.setText(time);
});
}
}
感谢 James_D 和 ItachiUchiha。它有效,如果我遗漏了什么,请告诉我!
编辑:
我还包括倒计时的代码,因为这是我最初的目标,也许有人会发现它也很有用:
public class CountdownController implements Initializable{
@FXML
private Label labTime;
@FXML
private Button buttSTOP;
private Timer timer = new Timer(true); //set it as a deamon
private int iHours = 0,
iMinutes = 1,
iSeconds = 10;
public void initCountdownController(int iHours, int iMinutes, int iSeconds){
this.iHours = iHours;
this.iMinutes = iMinutes;
this.iSeconds = iSeconds;
}
@Override
public void initialize(URL location, ResourceBundle resources) {
buttSTOP.setOnAction(e -> {
buttSTOPAction(e);
});
timer.schedule(new MyTimer(), 0, 1000);
}
private void buttSTOPAction(ActionEvent e) {
timer.cancel();
}
public class MyTimer extends TimerTask{
@Override
public void run() {
String time = iHours + ":" + iMinutes + ":" + iSeconds;
Platform.runLater(() -> {
labTime.setText(time);
});
if(iSeconds < 1)
if(iMinutes < 1)
if(iHours < 1)
this.cancel();
else{
iHours--;
iMinutes = 59;
iSeconds = 59;
}
else{
iMinutes--;
iSeconds = 59;
}
else
iSeconds--;
}
}
所以我遵循了这个教程:https://www.youtube.com/watch?v=gyyj57O0FVI
我在 javafx8 中编写了完全相同的代码。
public class CountdownController implements Initializable{
@FXML
private Label labTime;
@Override
public void initialize(URL location, ResourceBundle resources) {
new Thread(){
public void run(){
while(true){
Calendar calendar = new GregorianCalendar();
int hour = calendar.get(Calendar.HOUR);
int minute = calendar.get(Calendar.MINUTE);
int second = calendar.get(Calendar.SECOND);
String time = hour + ":" + minute + ":" + second;
labTime.setText(time);
}
}
}.start();
}
我关闭Window后,application/thread在系统中仍然是运行。我猜这是因为无限循环,但线程不应该随着应用程序关闭而终止吗?
第二件事是,当我尝试为 Label 设置文本时出现错误:
Exception in thread "Thread-4" java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-4
at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:204)
at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:364)
at javafx.scene.Parent.onProposedChange(Parent.java:364)
at com.sun.javafx.collections.VetoableListDecorator.setAll(VetoableListDecorator.java:113)
at com.sun.javafx.collections.VetoableListDecorator.setAll(VetoableListDecorator.java:108)
at com.sun.javafx.scene.control.skin.LabeledSkinBase.updateChildren(LabeledSkinBase.java:575)
at com.sun.javafx.scene.control.skin.LabeledSkinBase.handleControlPropertyChanged(LabeledSkinBase.java:204)
at com.sun.javafx.scene.control.skin.LabelSkin.handleControlPropertyChanged(LabelSkin.java:49)
at com.sun.javafx.scene.control.skin.BehaviorSkinBase.lambda$registerChangeListener(BehaviorSkinBase.java:197)
at com.sun.javafx.scene.control.skin.BehaviorSkinBase$$Lambda4/1099655841.call(Unknown Source)
at com.sun.javafx.scene.control.MultiplePropertyChangeListenerHandler.changed(MultiplePropertyChangeListenerHandler.java:55)
at javafx.beans.value.WeakChangeListener.changed(WeakChangeListener.java:89)
at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:182)
at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81)
at javafx.beans.property.StringPropertyBase.fireValueChangedEvent(StringPropertyBase.java:103)
at javafx.beans.property.StringPropertyBase.markInvalid(StringPropertyBase.java:110)
at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:143)
at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:49)
at javafx.beans.property.StringProperty.setValue(StringProperty.java:65)
at javafx.scene.control.Labeled.setText(Labeled.java:146)
at application.CountdownController.run(CountdownController.java:29)
...是的,我将阅读更多关于线程的内容,但我想知道这些问题的答案。
第一部分
线程在创建时独立于其他线程运行。你有一个无限循环的新线程,这意味着它会永远保持 运行,即使在舞台关闭后也是如此。
通常情况下,不建议使用死循环,因为跳出死循环非常困难。
建议您使用:
然后您可以调用其中之一(根据您使用的是什么)
当你的舞台关闭时。您可以使用类似 :
stage.setOnCloseRequest(closeEvent -> {
timertask.cancel();
});
JavaFX API的 (感谢James_D的评论)
这些不需要显式取消,因为 ScheduledService
使用守护线程并且 AnimationTimer
在 JavaFX 线程上运行。
第二部分
你的问题的第二部分已经在论坛中多次回答了。
You need to be on the JavaFX Application thread to use scene graph elements.
由于您创建了一个新线程并尝试更新 label
,这是一个 JavaFX 节点,它抛出了异常。更多信息,请访问:
JavaFX error when trying to remove shape
Why am I getting java.lang.IllegalStateException "Not on FX application thread" on JavaFX?
Javafx Not on fx application thread when using timer
就我而言,使用 ScheduledExecutorService
你不能轻易将它设置为守护进程,我不想玩 stage.setOnCloseRequest(closeEvent -> {});
使用 AnimationTimer,我无法像您建议的那样在迭代之间执行 Thread.sleep(100)
之类的操作,因为 "AnimationTimer runs on the JavaFX thread."
ScheduledService
现在对我来说很难理解...
所以,当我阅读和阅读它时,我得出结论,也许这个简单的选择是最好的:
public class CountdownController implements Initializable{
@FXML
private Label labTime;
@FXML
private Button buttSTOP;
@Override
public void initialize(URL location, ResourceBundle resources) {
Timer timer = new Timer(true); //set it as a deamon
timer.schedule(new MyTimer(), 0, 1000);
}
public class MyTimer extends TimerTask{
@Override
public void run() {
Calendar calendar = new GregorianCalendar();
int hour = calendar.get(Calendar.HOUR);
int minute = calendar.get(Calendar.MINUTE);
int second = calendar.get(Calendar.SECOND);
String time = hour + ":" + minute + ":" + second;
Platform.runLater(() -> {
labTime.setText(time);
});
}
}
感谢 James_D 和 ItachiUchiha。它有效,如果我遗漏了什么,请告诉我!
编辑: 我还包括倒计时的代码,因为这是我最初的目标,也许有人会发现它也很有用:
public class CountdownController implements Initializable{
@FXML
private Label labTime;
@FXML
private Button buttSTOP;
private Timer timer = new Timer(true); //set it as a deamon
private int iHours = 0,
iMinutes = 1,
iSeconds = 10;
public void initCountdownController(int iHours, int iMinutes, int iSeconds){
this.iHours = iHours;
this.iMinutes = iMinutes;
this.iSeconds = iSeconds;
}
@Override
public void initialize(URL location, ResourceBundle resources) {
buttSTOP.setOnAction(e -> {
buttSTOPAction(e);
});
timer.schedule(new MyTimer(), 0, 1000);
}
private void buttSTOPAction(ActionEvent e) {
timer.cancel();
}
public class MyTimer extends TimerTask{
@Override
public void run() {
String time = iHours + ":" + iMinutes + ":" + iSeconds;
Platform.runLater(() -> {
labTime.setText(time);
});
if(iSeconds < 1)
if(iMinutes < 1)
if(iHours < 1)
this.cancel();
else{
iHours--;
iMinutes = 59;
iSeconds = 59;
}
else{
iMinutes--;
iSeconds = 59;
}
else
iSeconds--;
}
}