Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to queue tasks in JavaFX?

I have made a GUI using JavaFX, and there are three radio buttons and once the user clicks submit and another thread is created and depending on what radiobutton was checked, the thread runs the required output and outputs the result to the console.

But while the thread is running (it takes around good 30 seconds for one process to complete) , I am able to check on any radiobutton. To it creates another thread and outputs long with the other ongoing thread. So my output box is just a jumble-wumble! I was looking at asynchronous task but I am not sure if that is something related to it.

Here is what I need: If a task is running, and I click on the submit button while it is running, wait for the previous task to END and THEN do the task.

Here is a psuedo code of my code

class TestMain {

    //main

    public void main(String ... args)  {

        launch(args);
    }

    /*declaring a new textfield with name m_status update here*/
    

    /*once submit button is clicked*/{

        //create a new thread 
        //to run   
    }
}

class ThreadBlahBlah implements Runnable {

    if(/*first checkbox was selected*/){
        //do these fancy stuff
        Platform.runLater(new Runnable() {
                @Override
                public void run() {
                    TestMain.m_status_update.setText("Test Completed!");
                }
        });
    
    }else if(/*second checkbox was selected*/){
        //do these other fancy stuff
        Platform.runLater(new Runnable() {
                @Override
                public void run() {
                    TestMain.m_status_update.setText("Test Completed!");

                }
            });
    }
}

Please do not recommend me to disable radio buttons while the task is running cause I want to queue my tasks like a linked list.

like image 453
Indigo Avatar asked Oct 28 '14 14:10

Indigo


People also ask

Is JavaFX multithreaded?

JavaFX provides a complete package to deal with the issues of multithreading and concurrency. There is an interface called Worker, an abstract class called Task, and ScheduledService for this purpose. The Task is basically a Worker implementation, ideal for implementing long running computation.

What is Task in JavaFX?

Tasks exposes additional state and observable properties useful for programming asynchronous tasks in JavaFX, as defined in the Worker interface. An implementation of Task must override the call() method. This method is invoked on the background thread.

Is JavaFX thread safe?

The JavaFX scene graph, which represents the graphical user interface of a JavaFX application, is not thread-safe and can only be accessed and modified from the UI thread also known as the JavaFX Application thread.

How do you create a task in Java?

Right-click Tasks and select New Task to launch the Task Creation Wizard. On the first panel in the wizard, click Create a Task by writing a Java class only to specify that you want to create a Java-based task, rather than a bean-based task. Click Next.


2 Answers

Use a single-threaded executor to run your tasks:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;

public class QueuedTaskExample extends Application {

    private AtomicInteger taskCount = new AtomicInteger(0);

    private ExecutorService exec = Executors.newSingleThreadExecutor(r -> {
        Thread t = new Thread(r);
        t.setDaemon(true); // allows app to exit if tasks are running
        return t ;
    });

    // Use the following if you want the tasks to run concurrently, instead of consecutively:

    // private ExecutorService exec = Executors.newCachedThreadPool(r -> {
    //     Thread t = new Thread(r);
    //     t.setDaemon(true);
    //     return t ;
    // });


    @Override
    public void start(Stage primaryStage) {

        // Just keep track of number of tasks pending/running for a status label:
        IntegerProperty pendingTasks = new SimpleIntegerProperty(0);

        Button startButton = new Button("Start");
        TextArea textArea = new TextArea();
        textArea.setEditable(true);
        startButton.setOnAction(event -> {
            Task<Void> task = createTask();
            // add text to text area if task's message changes:
            task.messageProperty().addListener((obs, oldMessage, newMessage) -> {
                textArea.appendText(newMessage);
                textArea.appendText("\n");
            });

            // for maintaining status label:
            pendingTasks.set(pendingTasks.get()+1);
            task.setOnSucceeded(taskEvent -> pendingTasks.set(pendingTasks.get()-1));

            // run task in single-thread executor (will queue if another task is running):
            exec.submit(task);
        });

        // layout etc
        HBox controls = new HBox(startButton);
        controls.setAlignment(Pos.CENTER);
        controls.setPadding(new Insets(10));

        Label statusLabel = new Label();
        statusLabel.textProperty().bind(Bindings.format("Pending/running tasks: %s", pendingTasks));

        BorderPane root = new BorderPane(textArea, statusLabel, null, controls, null);
        Scene scene = new Scene(root, 600, 400);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    @Override
    public void stop() {
        exec.shutdownNow();
    }

    // Trivial task that counts slowly to 5, updating its message as it goes:
    private Task<Void> createTask() {
        final int taskNumber = taskCount.incrementAndGet();
        return new Task<Void>() {
            @Override 
            public Void call() throws Exception {
                for (int count=1; count<=5; count++) {
                    Thread.sleep(1000);
                    updateMessage("Task "+taskNumber+": Count "+count);
                }
                return null ;
            }
        };
    }

    public static void main(String[] args) {
        launch(args);
    }
}
like image 73
James_D Avatar answered Oct 19 '22 05:10

James_D


When the user clicks on a radio button, first disable all radio buttons so the user will not be able to click on other radio buttons while your task is running.

When you finished with the background job, re-enable all radio buttons so the user can choose another task.

See Node.setDisabled() (RadioButton extends Node).

If you do need to queue tasks, your background thread should maintain a a task list, and when the user clicks, add the task to the list, which the background thread should consume (start another task if the current one is completed and there are more).

For advanced threaded execution see Executors and ExecutorService.

like image 39
icza Avatar answered Oct 19 '22 04:10

icza