正如我在标题中所说,我正在尝试使用 JavaFx 来实现生产者-消费者案例。 首先,我将展示问题所在:
- “主菜单”,带有“开始生产”、“停止生产”按钮和两个文本区域,每个文本区域用于显示生产者-消费者工作的结果(每个分支有 2 个)
- “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/