问题
I've been working on a robot simulator using javafx. It uses a separate thread to calculate where the robot and each of its parts should be after each time increment. The actual updating of the UI is handled with a call to Platform.runLater()
.
Here is an example of how a node (in this case a Rectangle) called leftFinger would be manipulated:
First, in the Controller class, create a Translate object and add it to the node's transforms:
leftFingerTranslateTransform = new Translate(0, 0);
leftFinger.getTransforms().add(leftFingerTranslateTransform);
Then, in the method that is passed into Platform.runLater()
do the following:
leftFingerTranslateTransform.setY(-40.0 * (armScale - 1.0));
leftFingerTranslateTransform.setX(fingerPos);
The above has worked well.
At one point, I accidentally put some of this node-repositioning code ( i.e., the calls to Tranlate.setX()
and Translate.setY()
) into a method that is called by the non-UI thread, without a call to Platform.runLater()
. To my surprise, this worked, with no problems. But I'm wondering if this could cause problems.
My searches for information about javafx and multithreading had led me to believe that the UI couldn't (or, at least shouldn't) be manipulated directly from a non-UI thread. Through experimentation, I've found that attempting to add a node to a scene from a non-UI thread causes an exception to be thrown, but manipulating (i.e., changing the properties of) a transform belonging to a node does not.
My question is: can the transforms belonging to a node safely have their properties changed from a non-UI thread (without using Platform.runLater()
)?
回答1:
Changing transforms on the node from a non-UI thread, when the node is attached to an active scene, could cause issues such as race conditions.
Don’t do it.
Explanation
From Wikipedia on race conditions:
Race conditions arise in software when an application depends on the sequence or timing of processes or threads for it to operate properly. As with electronics, there are critical race conditions that result in invalid execution and bugs. Critical race conditions often happen when the processes or threads depend on some shared state. Operations upon shared states are critical sections that must be mutually exclusive. Failure to obey this rule opens up the possibility of corrupting the shared state.
Here, the shared state is the list of transforms and stuff which is derived from them (such as the layout of the scene).
From the Node Javadoc, as linked by Slaw in comments:
Node objects may be constructed and modified on any thread as long they are not yet attached to a Scene in a Window that is showing. An application must attach nodes to such a Scene or modify them on the JavaFX Application Thread
The reason that it is unsafe is because the JavaFX UI thread runs on a timed pulse (see the JavaFX architecture doc to understand this), concurrently to any non-UI threads. During the timed pulse, the scene is rendered, CSS is applied, transforms are applied, animated properties are updated, etc. So if you modify the transforms on a non-UI thread while they are being applied during the pulse processing by the UI thread, you have a potential race condition, which can result in corrupted shared state, and the outcome is unpredictable and possibly quite undesirable.
来源:https://stackoverflow.com/questions/59446936/javafx-can-a-nodes-tranforms-be-safely-manipulated-from-a-non-ui-thread