Java 多线程应用程序在执行onclickBtn后挂起
我正在用javaFx编写一个天气应用程序,从openweather.org获取数据。从openweather获取JSON的整个代码运行良好,可以将JSON数据转换为对象。我使用lambda表达式在Java 多线程应用程序在执行onclickBtn后挂起,java,multithreading,javafx,freeze,Java,Multithreading,Javafx,Freeze,我正在用javaFx编写一个天气应用程序,从openweather.org获取数据。从openweather获取JSON的整个代码运行良好,可以将JSON数据转换为对象。我使用lambda表达式在Platform.runLater()中实现Runnable。问题是:如果我运行主类,按下按钮,应用程序将挂起。用于导入数据的线程工作(由控制台上的2个打印项检查),主线程“跳过”Platform.runLater()并在控制台上打印一些内容。我不确定这里出了什么问题 我的控制器类: package s
Platform.runLater()中实现Runnable
代码>。问题是:如果我运行主类,按下按钮,应用程序将挂起。用于导入数据的线程工作(由控制台上的2个打印项检查),主线程“跳过”Platform.runLater()代码>并在控制台上打印一些内容。我不确定这里出了什么问题
我的控制器类:
package sample;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import java.time.LocalTime;
import java.util.ArrayList;
public class Controller implements Observable{
private static final WeatherStation WEATHER_STATION = new WeatherStation();
protected volatile boolean isRunning = false;
private String response;
private static final int _5MINUTES = 1000*60*5;
private volatile ArrayList<Observer> observers = new ArrayList<>();
@FXML
private TextField cityTextfield;
@FXML
private CategoryAxis xAxis;
@FXML
private NumberAxis yAxis;
@FXML
private Button btn;
@FXML
private LineChart<String, Number> plot;
@FXML
void onclickBtn(ActionEvent event) throws InterruptedException {
WeatherUpdater weatherUpdater = new WeatherUpdater(plot);
setSettings();
WeatherObserver wroclaw = new WeatherObserver();
weatherUpdater.addObserver(wroclaw);
Platform.runLater(()->{
isRunning = true;
while(isRunning) try {
addObserver(wroclaw);
PlotDataUpdater<String, Number> dataUpdater = new PlotDataUpdater<>();
WEATHER_STATION.sendQuery();
response = WEATHER_STATION.getCurrentResponse();
updateObservers();
WeatherConditions weatherConditions = observers.get(0).getWeatherConditions();
LocalTime currentTime = LocalTime.of(LocalTime.now().getHour(), LocalTime.now().getMinute());
dataUpdater.updateSeries(currentTime.toString(), weatherConditions.getMainTemp());
dataUpdater.updatePlot(plot);
Thread.currentThread().sleep(_5MINUTES);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println("Thread interrupted");
}
});
System.out.println(".................................");
}
@Override
public void addObserver(Observer observer) {
if(!observers.contains(observer)) observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
if(observers.contains(observer)) observers.remove(observer);
}
@Override
public void updateObservers() {
for(Observer o : observers){
o.updateWeatherInfo(response);
}
}
private void setSettings(){
plot.getData().clear();
xAxis.setAutoRanging(true);
yAxis.setAutoRanging(true);
plot.setAnimated(false);
}
}
结合上述类修改“onclickBtn”方法:
@FXML
void onclickBtn(ActionEvent event) throws InterruptedException {
WeatherUpdater weatherUpdater = new WeatherUpdater(plot);
setSettings();
WeatherObserver wroclaw = new WeatherObserver();
weatherUpdater.addObserver(wroclaw);
weatherUpdater.start();
System.out.println(".................................");
}
控制台输出:
.................................
Server status: 200
Exception in thread "Weather updater" java.lang.IllegalStateException: Not on FX application thread; currentThread = Weather updater
at javafx.graphics/com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:291)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:424)
at javafx.graphics/javafx.scene.Parent$3.onProposedChange(Parent.java:471)
at javafx.base/com.sun.javafx.collections.VetoableListDecorator.add(VetoableListDecorator.java:206)
at javafx.controls/javafx.scene.chart.LineChart.seriesAdded(LineChart.java:405)
at javafx.controls/javafx.scene.chart.XYChart.lambda$new$1(XYChart.java:160)
at javafx.base/com.sun.javafx.collections.ListListenerHelper$SingleChange.fireValueChangedEvent(ListListenerHelper.java:164)
at javafx.base/com.sun.javafx.collections.ListListenerHelper.fireValueChangedEvent(ListListenerHelper.java:73)
at javafx.base/javafx.collections.ObservableListBase.fireChange(ObservableListBase.java:233)
at javafx.base/javafx.collections.ListChangeBuilder.commit(ListChangeBuilder.java:482)
at javafx.base/javafx.collections.ListChangeBuilder.endChange(ListChangeBuilder.java:541)
at javafx.base/javafx.collections.ObservableListBase.endChange(ObservableListBase.java:205)
at javafx.base/javafx.collections.ModifiableObservableListBase.setAll(ModifiableObservableListBase.java:90)
at javafx.base/javafx.collections.ObservableListBase.setAll(ObservableListBase.java:251)
at sample.PlotDataUpdater.updatePlot(PlotDataUpdater.java:36)
at sample.WeatherUpdater.run(WeatherUpdater.java:56)
at java.base/java.lang.Thread.run(Thread.java:834)
我想你误解了你的意图。它不用于运行后台任务,事实上它完全相反:它用于将任务委托给JavaFX应用程序线程(为了简单起见,在JavaFX应用程序中为“主线程”)
根据您的描述,我猜您想要做的是在后台线程中从web API获取数据,这样它就不会阻塞应用程序。要做到这一点,您可以使用,请注意,为了在以后更新GUI,您需要使用我认为您是对的。但是如果我创建了一个实现Runnable
和Observable
的类,将Thread
对象作为字段,实现start
,stop
,interrupt
,然后在方法oncklickBtn
中创建这个类的实例并启动线程,引发异常:线程“Weather updater”java.lang.IllegalStateException中的异常:不在FX应用程序线程上
。因此我使用了Platform.runLater
——只是没有抛出异常,数据是从网站获取的,但是应用程序被挂起了。你应该只在Platform.runLater
中包装最小的GUI更新代码,而不是整个方法。它挂起的原因是由于您的循环(while(isRunning
),应该在后台线程上运行。如果仍然有问题,请使用新代码更新您的问题。我添加了代码,其中我使用一个单独的类及其自己的线程将这些内容从网站中删除。我还粘贴了控制台输出。这两行:dataUpdater.updateSeries(currentTime.toString(),weatherConditions.getMainTemp())
和dataUpdater.updatePlot(绘图)
应该包装在平台中。runLater
。我不确定是否有必要将第一个放在平台中。runLater
,但为了干净起见,我仍然将其放在那里。在JavaFX中,出于线程安全原因,任何场景图的更新都应该在JavaFX应用程序线程上完成。有趣的是,我找不到任何更新除了在任务
的文档中提到这一点的官方文档。但是,仅允许在主线程上更新GUI的概念在多种编程语言和框架中都很常见。
.................................
Server status: 200
Exception in thread "Weather updater" java.lang.IllegalStateException: Not on FX application thread; currentThread = Weather updater
at javafx.graphics/com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:291)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:424)
at javafx.graphics/javafx.scene.Parent$3.onProposedChange(Parent.java:471)
at javafx.base/com.sun.javafx.collections.VetoableListDecorator.add(VetoableListDecorator.java:206)
at javafx.controls/javafx.scene.chart.LineChart.seriesAdded(LineChart.java:405)
at javafx.controls/javafx.scene.chart.XYChart.lambda$new$1(XYChart.java:160)
at javafx.base/com.sun.javafx.collections.ListListenerHelper$SingleChange.fireValueChangedEvent(ListListenerHelper.java:164)
at javafx.base/com.sun.javafx.collections.ListListenerHelper.fireValueChangedEvent(ListListenerHelper.java:73)
at javafx.base/javafx.collections.ObservableListBase.fireChange(ObservableListBase.java:233)
at javafx.base/javafx.collections.ListChangeBuilder.commit(ListChangeBuilder.java:482)
at javafx.base/javafx.collections.ListChangeBuilder.endChange(ListChangeBuilder.java:541)
at javafx.base/javafx.collections.ObservableListBase.endChange(ObservableListBase.java:205)
at javafx.base/javafx.collections.ModifiableObservableListBase.setAll(ModifiableObservableListBase.java:90)
at javafx.base/javafx.collections.ObservableListBase.setAll(ObservableListBase.java:251)
at sample.PlotDataUpdater.updatePlot(PlotDataUpdater.java:36)
at sample.WeatherUpdater.run(WeatherUpdater.java:56)
at java.base/java.lang.Thread.run(Thread.java:834)