java - JavaFx 中的生产者-消费者应用程序(从任务到 "update"两个文本字段获取正确值的问题)

标签 java multithreading javafx concurrency listener

正如我在标题中所说,我正在尝试使用 JavaFx 来实现生产者-消费者案例。 首先,我将展示问题所在:

基本上我得到了带有三个选项卡的 TabPane: enter image description here

  • “主菜单”,带有“开始生产”、“停止生产”按钮和两个文本区域,每个文本区域用于显示生产者-消费者工作的结果(每个分支有 2 个) enter image description here
  • “First Producer”用于输入生产者名称和生产时间 slider (基本为 Thread.sleep(long m) 的值)
  • “第二制作人”与第一制作人一模一样 那么,现在谈谈事情本身。在“接受”生产者和消费者的数据并开始生产消费之后,我遇到了将文本设置为生产者和消费者的 TextAreas 的问题。我尝试将它们绑定(bind)到任务,但没有成功(也许我做错了什么,我不知道)。

这也是我的代码:

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

}

生产者.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;
    }
}

消费者.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 和 Consumer 类都扩展了 Task 抽象类,并且都实现了抽象方法 call()。

如果您知道这个问题的答案(从线程生产者/消费者“发送”字符串数据,并相应地向这两个 TextArea 分配“任务”),我将非常感激。

最佳答案

So, for case of replacing "synchronise"

解决我在上面评论中提到的两个问题的一个解决方案是创建一个 Object仅用于同步,并将对象的引用传递给需要它的类的构造函数:

我不太确定您的总体目标是什么,但看起来您正在尝试创建两个生产者/消费者线程(即 consumer1 仅与 producer1 交互) ,并且 consumer2 仅与 producer2 交互。)如果这是真的,那么您将需要 lock1和一个 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);
        ...
    }
}

然后,在您的 Producer 中类,你可以这样做:

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

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

同样,在 Consumer 中执行相同的操作类,同样对 lock2 做同样的事情在 Controller 中。

现在,您的producer1和你的consumer1可以通过执行 synchronized(lock) 使用相同的共享锁对象,和lock.wait()lock.notify() ;和你的producer2和你的consumer2可以拥有自己的、不同的、共享的锁对象。

关于java - JavaFx 中的生产者-消费者应用程序(从任务到 "update"两个文本字段获取正确值的问题),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72251817/

相关文章:

java - Spring 的多线程

java - 如何根据单独类的进度更新 JavaFX 进度指示器?

JavaFX:暂停直到动画完成

java - 性能上有区别吗?

java - 对象转换

java - instanceof 运算符 - 为什么会出现非法编译时错误

java - 在 JavaFX 中获取屏幕上路径的 x y 坐标

java - 如何在 Java 8 中比较两个对象

c# - 涉及异步调用时如何跟踪工作单元?

c - 修改PID管理器以实现多线程