Java 多线程应用程序在执行onclickBtn后挂起

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

我正在用javaFx编写一个天气应用程序,从openweather.org获取数据。从openweather获取JSON的整个代码运行良好,可以将JSON数据转换为对象。我使用lambda表达式在
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)