I have an AnimationTimer, that I use as an infinite loop like this:
animator = new AnimationTimer(){
@Override
public void handle(long now) {
// Update
Game.simulationStep();
// Render
Game.render();
}
}
};
animator.start();
The handle function is called 60 times a second, updates a small Game and renders some views in a scenegraph.
What I´m asking myself is, if when I have Events like a button click, will the code of the event be executed after a loop iteration is done? Or is it multithreaded?
The thing is, I have a List of GameObjects which can be manipulated by the event (removing GameObjects by button click) but also by the logic in simulationStep. It could be problematic, if the event removes GameObjects from that List, while simulationStep() is doing something with the Objects in the List.
CodePudding user response:
AnimationTimer is not multithreaded and the handle()
method runs on the JavaFX application thread, so I guess if you put an infinite loop or a blocking call inside handle()
, it freezes the application. Answering your question, unless you have some behaviour that executes on other threads, it'll work fine.
Here's a related thread: Is AnimationTimer running in its own thread?
CodePudding user response:
The answer depends not on the code you provide but on your other code.
If the code is responding to events originating in the JavaFX framework (like a button press), then you don't need to worry, because those events are also on the JavaFX thread, the same as the animation timer.
You only need to worry if the event originates on a different thread outside of JavaFX. For example, an incoming chat message from a network chat client, or an AI loop that you are running on your own thread.
Also, you shouldn't use property listeners and bindings that might change values on another thread. For example, don't modify from another thread an observable list that is used to back a list view. The internal implementation of the observer and binding features assumes that the properties and listeners are all used on a single thread. Again, that is only something to worry about if your code is actually multi-threaded.
If you do really have multi-threaded code with events originating from other threads, use JavaFX concurrency, e.g. Task
and/or Platform.runLater
. Possibly in conjunction with a queue as in Seph's answer as demonstrated (somewhat), by this multi-threaded JavaFX logging framework using a queue. However, usually a separate queue is not required, because Platform.runLater
will add runnables to a built-in queue that JavaFX maintains for stuff to be run on the JavaFX thread later.
For your specific concern:
I have a List of GameObjects which can be manipulated by the event (removing GameObjects by button click) but also by the logic in simulationStep. It could be problematic, if the event removes GameObjects from that List, while simulationStep() is doing something with the Objects in the List.
No. It will not be problematic. Everything is running on a single thread, the GameObjects cannot be removed by button click while simulationStep()
is running, because the click handler and the simulationStep cannot be running at the same time.
A good summary of different options for executing periodic events and when and when not to involve multiple threads, plus how to handle them if they are used, is in Slaw's answer to:
I advise reading the JavaFX architecture overview sections on the Glass Windowing Toolkit, Threads, and Pulse (twice). The document explains how the JavaFX system works with respect to threading, event processing, and pulse handling (the AnimationTimer handle method is triggered by a pulse).
CodePudding user response:
You can add the events to a queue and process that queue first in your animation timer. This should avoid anytime of concurrency or race condition issues you may be concerned about without hurting anything if they do not actually exist.
animator = new AnimationTimer(){
@Override
public void handle(long now) {
//Process
Game.processEvents();
// Update
Game.simulationStep();
// Render
Game.render();
}
}
};
animator.start();