我有多个工作线程,以及一个报告这些线程中发生的事情的JavaFX GUI。
线程之间共享了大量数据,需要对其进行可视化。所以我使用ObservableList和Property来轻松地在JavaFX中显示数据。
我已经制作了一个小示例应用程序,以显示类似于我的应用程序中发生的事情。 它有2个列表,工作线程将数据从一个列表移动到另一个列表。状态字符串保持最新。 可以在http://codetidy.com/6569/找到完整的示例代码(此代码将崩溃,请参阅后面的内容)
这是共享的ObservableList的&属性:
private ObservableList<String> newItems;
private ObservableList<String> readyItems;
private StringProperty status;
以下是它们在JavaFX中的使用方式:
listViewA.setItems(processor.getNewItems());
listViewB.setItems(processor.getReadyItems());
statusLabel.textProperty().bind(processor.getStatus());
工作线程更新这些列表和属性,但当然,它需要在JavaFX线程上执行此操作,这就是事情变得丑陋的地方。 如果我不需要在JavaFX线程上更新,那么这将是代码:
Runnable newItemAdder = new Runnable() {
@Override
public void run() {
while(true) {
synchronized (newItems) {
String newItem = checkForNewItem(); //slow
if (newItem != null) {
newItems.add(newItem);
newItems.notify();
}
if (newItems.size() >= 5)
status.set("Overload");
else
status.set("OK");
}
synchronized (readyItems) {
if (readyItems.size() > 10)
readyItems.remove(0);
}
try { Thread.sleep(200); } catch (InterruptedException e) { return; }
}
}
};
new Thread(newItemAdder).start();
Runnable worker = new Runnable() {
@Override
public void run() {
while(true) {
List<String> toProcess = new ArrayList<String>();
synchronized (newItems) {
if (newItems.isEmpty())
try { newItems.wait(); } catch (InterruptedException e) { return; }
toProcess.addAll(newItems);
}
for (String item : toProcess) {
String processedItem = processItem(item); //slow
synchronized (readyItems) {
readyItems.add(processedItem);
}
}
}
}
};
new Thread(worker).start();
当然,使用Platform.runLater可以轻松解决一些问题:
Platform.runLater(new Runnable() {
@Override
public void run() {
synchronized (newItems) {
if (newItems.size() >= 5)
status.set("Overload");
else
status.set("OK");
}
}
});
这对于我只在任务中写入的属性/列表来说很好,只能在JavaFX GUI中读取。 但是对于此示例中的列表执行此操作变得非常复杂,您需要对其进行同步,读取和写入。你需要添加很多Platform.runLater,你需要阻止,直到“runLater”任务完成。这会导致非常复杂且难以读写的代码(我设法以这种方式运行此示例,请参阅我的意思:http://codetidy.com/6570/)。
还有其他方法让我的榜样有效吗?我很感激任何其他解决方案或部分解决方案...
答案 0 :(得分:15)
背景资讯
Task javadoc包括许多用于在JavaFX中的线程之间传递数据的并发使用模式。
Task包括便捷数据传输方法,例如updateMessage,可以用来代替具有用户定义状态属性的Runnable。
适当时,请考虑使用专为并发而设计的集合结构,例如BlockingQueue。另一个优点是BlockingQueues可以有大小限制,这似乎是你想要的。
一些一般性建议
以上只是经验法则,不需要在教学上遵循。
合理复杂的线程样本
答案 1 :(得分:1)
完整示例的原始链接和jewelsea的示例解决方案已经死了,所以我将添加一个答案,简要总结一下我最终做的事情。
为了简单起见,我们假设你从一个包含数据模型的类开始(让我们称之为DataModel
)。这个类的一个实例由多个改变它的线程使用。
现在的问题是你想在javaFX中使用数据模型,但你不能简单地改变你的数据模型以使用Property
和ObservableList
等。如果这样做,听众将是从非javafx线程调用,绑定到它们的GUI将抛出异常。
相反,您需要为javaFX创建一个单独的数据模型类。这只是原始版本的JavaFX版本(我们称之为FXDataModel
)。因此,此版本包含相同的信息,但它使用javaFX Property
和ObservableList
。这样,您就可以将GUI绑定到它。
下一步是使用FXDataModel
实例定期更新DataModel
实例。
为此,您需要向update(DataModel dataModel)
方法添加FXDataModel
方法,该方法将原始数据模型中的数据复制到FXDataModel
实例中。必须始终在javaFX线程上调用此更新函数。最后,您需要做的就是定期调用该更新函数。
在我的实际场景中,我每200毫秒调用一次更新函数,这足以能够在GUI中显示数据模型的实时视图。 (如果您想要的不仅仅是数据模型的视图,并且您想要从GUI更改内容,那么事情会变得更复杂,但这不是我需要做的事情)