Thread Issues in Java FX

Asked

Viewed 301 times

1

Good night,

I am having problems trying to implement a thread to my application, I always get the same error and do not know how to solve.

When I run the application the first Thread takes effect, but when it runs the second it generates the error.

Follow a class example:

package ajuda;

import java.net.URL;
import java.util.ResourceBundle;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;

public class AjudaController implements Initializable {

    @FXML
    private Label texto;

    private void Thread1() {

        Service service = new Service() {
            @Override
            protected Task createTask() {
                return new Task() {
                    @Override
                    protected Object call() throws Exception {

                        texto.setText("Alguma coisa");
                        Thread2();
                        return null;
                    }
                };
            }
        };
        service.start();
    }

    private void Thread2() {

        Service service = new Service() {
            @Override
            protected Task createTask() {
                return new Task() {
                    @Override
                    protected Object call() throws Exception {

                        texto.setText("Alguma coisa 2");
                        return null;
                    }
                };
            }
        };
        service.start();
    }

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        Thread1();
    }
}

Every time I run I get the same error:

mai 24, 2018 12:14:53 AM javafx.concurrent.Service lambda$static$488
WARNING: Uncaught throwable in javafx concurrent thread pool
java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-5
    at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:279)
    at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:423)
    at javafx.scene.Parent$2.onProposedChange(Parent.java:367)
    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$61(BehaviorSkinBase.java:197)
    at com.sun.javafx.scene.control.MultiplePropertyChangeListenerHandler$1.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:144)
    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:145)
    at ajuda.AjudaController$2$1.call(AjudaController.java:50)
    at javafx.concurrent.Task$TaskCallable.call(Task.java:1423)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at javafx.concurrent.Service.lambda$null$493(Service.java:725)
    at java.security.AccessController.doPrivileged(Native Method)
    at javafx.concurrent.Service.lambda$executeTask$494(Service.java:724)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

2 answers

1

Any changes you make to elements drawn on the screen go through the FX Application Thread, which is only 1 thread. The service class, while interacting well with the FX Application Thread, see some of the documentation below:

A Service is a non-visual Component encapsulating the information required to perform some work on one or more background threads. As part of the Javafx UI library, the Service Knows about the Javafx Application thread and is Designed to relieve the application Developer from the burden of Managing multithreaded code that interacts with the user interface. As such, all of the methods and state on the Service are intended to be Invoked exclusively from the Javafx Application thread. [...]. However, Once the Service has been initialized and Started, it may only thereafter be used from the FX thread.

Source: https://docs.oracle.com/javase/8/javafx/api/javafx/concurrent/Service.html

What probably happened was that the two services were occupying the FX Thread and tried to change the same component at the same time. If this is really necessary, and not a design error, you can capture the status change by using a stateProperty() from Service 1 to SUCCEEDED or FAILED before running Service 2.

  • I understand, but I can solve by throwing my function to another class and running it in a service within the main class. My problem now is that I can’t give a setText this way, my other class calls a method in my main class that generates a setText, but it just doesn’t work...

-2

Use: Platform.runLater(() -> text.setText("Something"));

  • It is a possible solution, but considering the API that already exists is gambiarra. The answer that already exists is preferable because it addresses the problem respecting the Service API that Javafx has and theoretically already has support for dealing with GUI-driven instructions on FX Application Thread (I’m not familiar with the API, but that’s what makes the response and documentation quoted).

Browser other questions tagged

You are not signed in. Login or sign up in order to post.