Thread Problems in Java FX

1

Good evening,

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

When I run the application the first Thread goes into effect, but when it executes the second it generates the error.

Here is a sample class:

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)
    
asked by anonymous 24.05.2018 / 05:21

1 answer

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, although it interacts 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 highlight 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 be used after the FX thread.

Source: link

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, not a design error, you can capture the change of state using a listener in stateProperty() of Service 1 to SUCCEEDED or FAILED before running Service 2.

    
24.05.2018 / 16:14