Producer-Consumer apllication on JavaFx (problem with getting correct values from tasks to "update" two text fields)

Viewed 81

As I said in title, I'm trying to make Producer-Consumer case with the usage of JavaFx. For the start I will show what the problem is:

Basically I got TabPane with three tabs: enter image description here

  • "Main Menu" with Buttons to "start production", "stop production" and two TextAreas each for showing results of producers-consumers work (there will be 2 for each branch) enter image description here
  • "First Producer" for typing producer name and slider for time of production (basically value for Thread.sleep(long m))
  • "Second Producer" is exactly like the first So, now to the matter itself. After "accepting" data of both producers and starting production-consumption I got stuck on a problem with settingText to both TextAreas of producers and consumers. I tried binding them to tasks, but that didn't work out (maybe I did something wrong, I don't know).

Here is my code also:

Controller.java:

package com.example.case4javafx;

import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;

import java.net.URL;
import java.util.LinkedList;
import java.util.ResourceBundle;

public class Controller implements Initializable {

    private LinkedList<String> listOfProducts = new LinkedList<>();

    @FXML
    private Slider sldFirstProd, sldSecondProd;

    int mSldFirstProdValue, mSldSecondProdValue;

    @FXML
    private TextField txtFirstProd, txtSecondProd;

    @FXML
    private static TextArea txtProdInfo, txtConsInfo;

    private String nameOfProd1, nameOfProd2;
    private int timeOfProd1, timeOfProd2;

    private Producer producer1, producer2;
    private Consumer consumer1, consumer2;

    @FXML
    private Button btnStartProduction, btnStopProduction, btnFirstProdClear, btnFirstProdAccept, btnSecondProdClear, btnSecondProdAccept;

    @FXML
    private Label lblSldFirstProd, lblSldSecondProd;




    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {
        sldFirstProd.valueProperty().addListener(new ChangeListener<Number>() {
            @Override
            public void changed(ObservableValue<? extends Number> observableValue, Number number, Number t1) {
                mSldFirstProdValue = (int) sldFirstProd.getValue();
                lblSldFirstProd.setText(mSldFirstProdValue + " ms");
            }
        });


        sldSecondProd.valueProperty().addListener(new ChangeListener<Number>() {
            @Override
            public void changed(ObservableValue<? extends Number> observableValue, Number number, Number t1) {
                mSldSecondProdValue = (int) sldSecondProd.getValue();
                lblSldSecondProd.setText(mSldSecondProdValue + " ms");
            }
        });
    }

    public void onStartProduction(ActionEvent e) throws InterruptedException {
        if(btnFirstProdAccept.isDisabled() && btnSecondProdAccept.isDisabled()) {
            producer1 = new Producer(listOfProducts, nameOfProd1, timeOfProd1);
            Thread t1 = new Thread(producer1);

            consumer1 = new Consumer(listOfProducts);
            Thread t2 = new Thread(consumer1);


            producer2 = new Producer(listOfProducts, nameOfProd1, timeOfProd1);
            Thread t3 = new Thread(producer2);

            consumer2 = new Consumer(listOfProducts);
            Thread t4 = new Thread(consumer2);

            t1.setDaemon(true);
            t2.setDaemon(true);
            t3.setDaemon(true);
            t4.setDaemon(true);

            t1.start();
            t2.start();
            t3.start();
            t4.start();

            btnStartProduction.setDisable(true);
        }

    }

    public void onStopProduction(ActionEvent e) {
        if(producer1 != null) {
            producer1.cancel();
        }
        if(producer2 != null) {
            producer2.cancel();
        }
        if(consumer1 != null) {
            consumer1.cancel();
        }
        if(consumer2 != null) {
            consumer2.cancel();
        }
        setEnableDisableProdData(txtFirstProd, sldFirstProd, true);
        setEnableDisableProdData(txtSecondProd, sldSecondProd, true);
        setEnableDisableProdButt(btnFirstProdClear, btnFirstProdAccept, true);
        setEnableDisableProdButt(btnSecondProdClear, btnSecondProdAccept, true);
        btnStartProduction.setDisable(false);
    }

    public void onClearFirstProducer(ActionEvent e) {
        setClearProd(txtFirstProd, sldFirstProd, lblSldFirstProd);
    }

    public void onAcceptFirstProducer(ActionEvent e) {
        nameOfProd1 = txtFirstProd.getText();
        timeOfProd1 = mSldFirstProdValue;
        setEnableDisableProdButt(btnFirstProdClear, btnFirstProdAccept, false);
        setClearProd(txtFirstProd, sldFirstProd, lblSldFirstProd);
        setEnableDisableProdData(txtFirstProd, sldFirstProd, false);
    }

    public void onClearSecondProducer(ActionEvent e) {
        setClearProd(txtSecondProd, sldSecondProd, lblSldSecondProd);
    }

    public void onAcceptSecondProducer(ActionEvent e) {
        nameOfProd2 = txtSecondProd.getText();
        timeOfProd2 = mSldSecondProdValue;
        setEnableDisableProdButt(btnSecondProdClear, btnSecondProdAccept, false);
        setClearProd(txtSecondProd, sldSecondProd, lblSldSecondProd);
        setEnableDisableProdData(txtSecondProd, sldSecondProd, false);
    }

    public void setEnableDisableProdData(TextField txtProdName, Slider sldProd, boolean toActivDeactiv) {
        if(toActivDeactiv) {
            txtProdName.setDisable(false);
            sldProd.setDisable(false);
        } else {
            txtProdName.setDisable(true);
            sldProd.setDisable(true);
        }
    }

    public void setEnableDisableProdButt(Button clear, Button accept, boolean toActivDeactiv) {
        if(toActivDeactiv) {
            clear.setDisable(false);
            accept.setDisable(false);
        } else {
            clear.setDisable(true);
            accept.setDisable(true);
        }
    }

    public void setClearProd(TextField txtProdName, Slider sldProd, Label sldValueInfo) {
        txtProdName.setText("");
        sldProd.setValue(0);
        sldValueInfo.setText("500 ms");
    }

    public static void setTxtAreaOfProd(String input) {
        txtProdInfo.setText(input);
    }

    public static void setTxtAreaOfCons(String input) {
        txtConsInfo.setText(input);
    }

}

Producer.java:

package com.example.case4javafx;

import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.scene.control.TextArea;

import java.util.LinkedList;
import java.util.List;

public class Producer extends Task<Void> {


    private final LinkedList<String> dataOfProducts;
    private final String nameOfProducer;
    private final int timeOfProduction;

    private final int capacity;

    private Object lock;

    public Producer(LinkedList<String> refToData, String nameOfProducer, int timeOfProduction, Object lock) {
        this.dataOfProducts = refToData;
        this.nameOfProducer = nameOfProducer;
        this.timeOfProduction = timeOfProduction;
        this.capacity = 10;
        this.lock = lock;
    }

    @Override
    protected Void call() throws InterruptedException {
        int value = 0;
        while (true) {
            synchronized (lock) {
                System.out.println("I'm a producer");
                if (isCancelled()) {
                    updateMessage("Cancelled");
                    break;
                }
                while (dataOfProducts.size() == capacity) {
                    lock.wait();
                }
                updateMessage("Producer produced-" + value);

                // to insert the jobs in the list
                String itemToAdd = nameOfProducer + " " + Integer.toString(value++);
                dataOfProducts.add(itemToAdd);
                // notifies the consumer thread that
                // now it can start consuming
                lock.notify();

                // makes the working of program easier
                // to  understand
                try {
                    Thread.sleep(this.timeOfProduction);
                } catch (InterruptedException e) {
                    if (isCancelled()) {
                        updateMessage("Cancelled");
                        break;
                    }
                }
            }
        }
        return null;
    }
}

Consumer.java:

package com.example.case4javafx;

import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.scene.control.TextArea;

import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;

public class Consumer extends Task<Void> {


    private final Object lock;
    private final LinkedList<String> dataOfProducts;

    public Consumer(LinkedList<String> dataOfProducts, Object lock) {
        this.dataOfProducts = dataOfProducts;
        this.lock = lock;
    }

    @Override
    protected Void call() throws InterruptedException {
        int value = 0;
        while (true) {
            synchronized (lock) {
                System.out.println("I'm a consumer");
                if (isCancelled()) {
                    updateMessage("Cancelled");
                    break;
                }
                while (dataOfProducts.size() == 0) {
                    lock.wait();
                }

                // to insert the jobs in the list
                String resultOfProducer = dataOfProducts.removeFirst();
                // notifies the consumer thread that
                // now it can start consuming
                lock.notify();

                // makes the working of program easier
                // to  understand
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    if (isCancelled()) {
                        updateMessage("Cancelled");
                        break;
                    }
                }
            }
        }
        return null;
    }
}

Producer and Consumer classes are extending Task abstract class and both are implementing abstract method call().

If you know the answer to this problem ("sending" String data from Thread Producer / Consumer with assignment "mission" to both of this TextAreas accordingly) I would be very thankful.

1 Answers

So, for case of replacing "synchronise"

One solution to two of the problems that I mentioned in comments above, is to create an Object that is used only for synchronization, and pass a reference to the object in to the constructors of classes where it is needed:

I'm not really sure what your overall goal is, but it looks like you are trying to create two producer/consumer pairs of threads (I.e., consumer1 interacts only with producer1, and consumer2 interacts only with producer2.) If that's true, then you're going to need a lock1 and a lock2:

...
private Object lock1, lock2;
private Producer producer1, producer2;
private Consumer consumer1, consumer2;
...
public void onStartProduction(ActionEvent e) throws InterruptedException {
    if(btnFirstProdAccept.isDisabled() && btnSecondProdAccept.isDisabled()) {
        lock1 = new Object();
        producer1 = new Producer(listOfProducts, nameOfProd1, timeOfProd1, lock1);
        Thread t1 = new Thread(producer1);
        consumer1 = new Consumer(listOfProducts, lock1);
        Thread t2 = new Thread(consumer1);
        ...
    }
}

Then, in your Producer class, you can do something like this:

...
private final int capacity;
private final Object lock;

public Producer(LinkedList<String> refToData, String nameOfProducer, int timeOfProduction, Object lock) {
    ...
    this.lock = lock;
    }

Likewise, do the same thing in your Consumer class, and likewise do the same thing for lock2 in the controller.

And now, your producer1 and your consumer1 can use the same shared lock object by doing synchronized(lock), and lock.wait() and lock.notify(); and your producer2 and your consumer2 can have their own, distinct, shared lock object.

Related