JavaFX中的复杂并发:使用来自多个工作线程的ObservableLists和Properties [英] Complex concurrency in JavaFX: using ObservableLists and Properties from multiple worker threads

查看:149
本文介绍了JavaFX中的复杂并发:使用来自多个工作线程的ObservableLists和Properties的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有多个工作线程和一个JavaFX GUI,它报告这些线程中发生的事情。

I have multiple worker threads, and a JavaFX GUI which is reporting on what is happening in these threads.

线程之间共享了大量数据需要可视化。所以我使用ObservableList和Property来轻松地在JavaFX中显示数据。

There is a lot of data shared between threads and it needs to be visualized. So I'm using ObservableList's and Property's to be able to easily show the data in JavaFX.

我做了一个小示例应用程序来显示类似于我的应用程序。
它有2个列表,工作线程将数据从一个列表移动到另一个列表。状态字符串保持最新。
完整的示例代码可以在 http://codetidy.com/6569/ 找到(此代码会崩溃,稍后会看到)

I've made a small example app to show something similar to what happens in my application. It has 2 lists and the worker thread moves data from one list to the other. A status String is kept up to date. Full example code can be found at http://codetidy.com/6569/ (this code will crash, see later)

这是共享的ObservableList的&属性:

Here are the shared ObservableList's & Properties:

private ObservableList<String> newItems;
private ObservableList<String> readyItems;
private StringProperty status;

以下是它们在JavaFX中的使用方式:

Here's how they are used in JavaFX:

listViewA.setItems(processor.getNewItems());
listViewB.setItems(processor.getReadyItems());
statusLabel.textProperty().bind(processor.getStatus());

工作线程更新这些列表和属性,但当然,它需要在JavaFX上执行此操作线程,这就是事情变得丑陋的地方。
如果我不需要在JavaFX线程上更新,这将是代码:

The worker thread updates these lists and properties, but off course, it needs to do this on the JavaFX thread, and that's where things get ugly. This would be the code if I didn't have to update on the JavaFX thread:

  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可以轻松解决一些问题:

Off course, some things are easy to solve with 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 / )。

That's fine for properties/lists that I only write to in the task, and only read in the JavaFX GUI. But it gets very complicated to do it for the lists in this example, on which you need to synchronize, read and write. You need add a lot of Platform.runLater and you need to block until the "runLater" task has finished. This results in very complex and hard to read and write code (I managed to get this example running this way, see what I mean: http://codetidy.com/6570/).

还有其他方法让我的例子有效吗?我很感激任何其他解决方案或部分解决方案...

Are there any other ways to get my example working? I'd appreciate any other solution or partial solutions...

推荐答案

背景信息

任务javadoc 包括许多用于在JavaFX中的线程之间传递数据的并发使用模式。

Task javadoc includes numerous concurrency usage patterns for passing data between threads in JavaFX.

任务包括便利数据传输方法,例如 updateMessage ,可以用来代替具有用户定义状态属性的Runnable。

Task includes convenience data transfer methods such as updateMessage and can be used instead of your Runnable with the user defined status property.

在适当的时候,考虑使用专为并发而设计的集合结构,例如 BlockingQueue 。另一个优点是BlockingQueues可以有大小限制,这似乎是你想要的。

When appropriate, consider using a collection structure designed for concurrency, such as a BlockingQueue. An additional advantage is that BlockingQueues can have size limits, which seems like something you want.

一些一般建议


  1. 在多个线程中使用可变的可观察项时要非常小心。很容易无意中触发导致竞争条件的更新,应用程序线程中活动场景图的更新以及其他线程问题。

  2. 尽可能使用不可变数据而不是可变的可观察项目。

  3. 利用 JavaFX并发中的一些更高级别的实用程序 java.util.concurrent 库。

  4. 尽可能避免显式同步和通知语句。

  5. 小心将同步或其他可能的阻塞语句放在运行于JavaFX应用程序线程 - 因为您可能会使GUI无响应。

  6. 使用JavaFX并发只有在需要与JavaFX线程进行交互时才会使用实用程序。

  7. 使用标准Java并发实用程序从JavaFX线程执行大量非常复杂的多线程处理。有一个协调JavaFX任务来合并和控制UI反馈。

  1. Be very careful when using mutable observable items in multiple threads. It is easy to inadvertently trigger updates that result in race conditions, updates to the active scene graph off the application thread and other threading issues.
  2. As much as possible, use immutable data rather than mutable observable items.
  3. Leverage some of the higher level utilities from the JavaFX concurrency and java.util.concurrent libraries.
  4. Avoid explicit synchronization and notify statements as much as possible.
  5. Be careful placing synchronization or other potentially blocking statements in code that runs on the JavaFX Application Thread - as you may make your GUI unresponsive.
  6. Use the JavaFX concurrency utilities only when you need interaction with the JavaFX thread.
  7. Do a lot of your very complicated multi-threaded processing off of the JavaFX thread using standard Java concurrency utilities. Have a single co-ordinating JavaFX Task for coalescing and controlling UI feedback.

以上只是经验法则,不需要在教学上遵循。

The above are just rules of thumb and don't need to be followed didactically.

合理复杂的线程样本


  • 图表渲染器,演示了上述一些原则,可以在保留UI响应进度更新和用户交互。

  • A chart renderer that demonstrates some of the principles above to render 300 charts while still keeping the UI responsive to progress updates and user interaction.

这篇关于JavaFX中的复杂并发:使用来自多个工作线程的ObservableLists和Properties的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆