1

I've put together a simple publish/subscribe pattern to allow multiple JavaFX classes to be dynamically instantiated and run. Each of these classes (a 'subscriber') is meant to visualize data coming from a model (in the model/view/controller sense) which has the role of 'publisher'. Data for visualization comes in the form of immutable objects, and is passed from the model (running in it's own thread) to a 'buffer' (also running in a separate thread) via a LinkedBlockingQueue. The Buffer then re-publishes all of the data from the model by putting it into LinkedBlockingQueues that are being emptied by threads responsible for running JavaFX visualizations.

So, the data path looks like this:

Model produces immutable objects ----(LinkedBlockingQueue)---> Buffer consumes objects and puts them on multiple queues ====(LinkedBlockingQueue) ===> Visualization thread consumes objects, does some pre-processing, and makes data available to a Timeline animation, which periodically updates the chart.

The entry point for the program extends Application, and its start() method builds all the JavaFX GUI components like so:

for (ModelVisualization viz : vizList) {
    viz.buildGUI();
    new Thread(viz).start();
}

The threads you see being started are the final consumers of the data in the description of the data-path above.

The problem: The visualizations show up and build properly, but nothing in any JavaFX window updates until the model thread goes to sleep. I can use System.err.println() statements to verify that the data from the model is moving through the various threads without deadlock, and I can see that the visualization threads are consuming everything properly. However, unless the model thread hits a Thread.sleep(100) or similar, the method associated with the Timeline never runs.

The model thread itself is started in the entry point constructor like so:

    Thread simThread = new Thread(new Runnable() {
        @Override
        public void run() {
            model.runSim();
        }
    });
    simThread.setPriority(Thread.MIN_PRIORITY);
    simThread.start();

...And only after that is the JavaFX stuff started up with:

launch();

This should, if I understand things correctly, have the model thread running independently of JavaFX. According to my system resource monitor, I have four whole unused cores (using a Core i7 on OSX) when this program is running, so it doesn't look like I'm starved for resources.

I do not know why JavaFX is paralyzed when my model thread is running. Sleeping my model thread is also something I'd like to avoid. Can anyone shed some light on what's going on, or how I might get JavaFX running properly?

ROMANIA_engineer
  • 54,432
  • 29
  • 203
  • 199
amm
  • 343
  • 3
  • 11

1 Answers1

1

Java FX is single threaded. You need to do Thread management in a special way for FX applications. Have a look here, and here.

Basically, you want to update your view, however, the view (thread) cannot synchronize with your other threads. Use the Platform tools in order to "play" with other threads.

Community
  • 1
  • 1
ra2085
  • 814
  • 4
  • 13
  • I find some of this documentation confusing. The javadoc of javafx.concurrent.Task notes that the update* methods can be called from any thread, but it seems to be exclusively called from call() in the examples. What is the significance of that? Why bother with a Task? The javadoc also explicitly states that Tasks should not interact directly with the UI, but what is "indirect interaction", then? Is it Platform.runLater() or Property updates? Neither of these options seem like good ones for constantly adding values to a chart Series. javafx.animation.Timeline is also never mentioned. – amm Jun 03 '14 at 14:37
  • What is the significance of that? -> It just states that the function is thread-safe (invocations will be "run later" in the application thread). Why bother with a Task? The sample link on stackoverflow that i provided explains it clearly: runLater for really simple stuff only (not multithread), task must be used for complex stuff (multithreaded/long running) why? because FX is single threaded, meaning the runnables event queue would be enormous by using multithreaded code in a Platform.runLater(). – ra2085 Jun 03 '14 at 22:19
  • but what is "indirect interaction", then? The doc only states a recommendation. If you really wish to have tight coupled code with your UI, then use Platform.runLater inside the call() task method to be able to reflect changes resulting from the task execution on the UI. – ra2085 Jun 03 '14 at 22:26
  • Property updates are generally the appropriate pattern in FX. I strongly recommend you to revisit your UI model, and assess where you can do improvements to be able to do property bindings and updates in long running/complex tasks. – ra2085 Jun 03 '14 at 22:29
  • "Neither of these options seem like good ones" Why? In my opinion, you're not using the provided FX utilities on your model to be able to use the UI thread. – ra2085 Jun 03 '14 at 22:39