多线程应用程序在执行onclickBtn后挂起 [英] Multi thread app hangs after executing onclickBtn

查看:116
本文介绍了多线程应用程序在执行onclickBtn后挂起的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在javaFx中编写一个天气应用程序,从openweather.org获取数据。从openweather获取JSON的整个代码工作正常,将JSON数据转换为对象。我使用lambda表达式在 Platform.runLater(); 中实现 Runnable 。问题是:如果我运行Main类,按下按钮,应用程序挂起。用于导入数据的线程(通过控制台上的2个打印检查)和主线程跳过 Platform.runLater(); 并在控制台上打印一些内容。我不确定这里有什么问题。

I'm writing a weather app in javaFx getting data from openweather.org. The whole code for getting JSON from openweather works fine, converting JSON data to an object too. I used lambda expression to implement Runnable in Platform.runLater();. The problem is: if I run the Main class, press the button, the app hangs. The thread for importing data works (checked by 2 prints on console) and the main thread "skips" Platform.runLater(); and prints something on the console. I'm not sure what is wrong here.

我的Controller类:

my Controller class:

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);
    }

}

用于生成绘图数据的类,从JSON转换的东西按预期工作。如果需要更多代码,请发表评论。

Classes used for generating plot data, converting things from JSON work as intended. If more code is needed, leave a comment.

@Edit:实现 Observable 的类的代码, Runnable 有自己的主题:

@ The code for a class implementing Observable, Runnable with own thread:

package sample;

import javafx.scene.chart.XYChart;

import java.time.LocalTime;
import java.util.ArrayList;

public class WeatherUpdater implements Runnable, Observable{

    private Thread updater;
    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<>();
    private XYChart<String, Number> plot;

    public WeatherUpdater(XYChart<String, Number> plot) {
        this.plot = plot;
    }

    public WeatherUpdater() {

    }

    public void start(){
        updater = new Thread(this,"Weather updater");
        updater.start();
    }

    public void interrupt(){
        isRunning = false;
        updater.interrupt();
    }

    public XYChart<String, Number> getPlot() {
        return plot;
    }


    @Override
    public void run() {
        isRunning = true;
        while(isRunning){
            try{
                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.sleep(_5MINUTES);
            } catch (InterruptedException e) {
                updater.interrupt();
                System.out.println("Thread interrupted" );
            }
        }
    }

    @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);
        }
    }
}

'onclickBtn'方法的修改结合上面的类:

Modification of 'onclickBtn' method combined with the class above:

@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)


推荐答案

我认为你误解了 Plaftform.runLater 适用于。它不是用于运行后台任务,实际上它完全相反:它用于将任务委托给JavaFX应用程序线程(或者为了简单起见,在JavaFX应用程序中使用主线程)。

I think you misunderstood what Plaftform.runLater is intended for. It's not for running background tasks, in fact it's the complete opposite: it's for delegating tasks to the JavaFX application thread (Or "main thread" in a JavaFX app for simplicity).

根据您的描述,我猜你想要做的是在后台线程中从Web API获取数据,这样它就不会阻止应用程序。要执行此操作,您可以使用 JavaFX任务,注意为了在以后更新GUI,您需要使用 Plaftform.runLater

Based on your description I would guess that what you want to do is to fetch the data from the web API in a background thread so that it doesn't block the application. To do this you can use JavaFX Task, note that in order to update the GUI afterwards you need to use Plaftform.runLater

这篇关于多线程应用程序在执行onclickBtn后挂起的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆