Zwar wurde hier das wichtige schon erwähnt, aber ich versuche mich dennoch mal an einer genaueren Erklärung.
Aus Threads heraus zeichnet man für gewöhnlich nicht. Es ist zwar sowohl mit OpenGL als auch mit DirectX (welches JavaFX verwendet unter Unix bzw. Windows) möglich von verschiedenen Threads zu zeichnen, aber man sollte genau wissen was man macht. Daher ist die allgemeine Empfehlung für gewöhnlich don't do it.
Daher stellt JavaFX dafür diverse Mechanismen vor, die Funktion runLater und die Task Klasse. runLater registriert ein Runnable objekt in einer Message queue, welches der JavaFX main Thread abbarbeitet sobald er Zeit dafür findet. Jedes runLater wird früher oder später ausgeführt werden (außer du hast nen Deadlock, dann hast du aber noch ganz andere Probleme).
Damit kann man aber auch stark Performance einbüßen, da man die Message Queue von JavaFX flooden kann, da jedes Runnable Objekt ausgeführt wird. Wenn dein Thread also schneller updated als JavaFX zeichnen kann geht das ganz schnell in die Hose gehen (das kann schnell passieren wenn die Threads auf verschiedenen CPU's laufen).
Ein Task hingegen hat einen State (also Informationen wie Progress und Message) welche an Properties gebindet werden können, sodass javaFX, sobald es zeit hat, überprüft ob was neues im Task steht, und dann die gebindete property updated.
Das bedeutet aber auch das alle Änderungen zwischen zwei abfragen nicht beachtet werden. Somit läufst du nicht in die Performance Probleme von runLater.
Ansonsten wenn du zwischen Threads kommunizieren willst (also nicht unbedingt GUI zeichnen) solltest du bevor du auf Daten eines anderen Threads zugreifst unbedingt

, oder über

den Thread ansteuern
Zu der Anzahl an Threads, die ist durch verschiedene Faktoren limitiert, CPU zahl gehört nicht dazu (vor allem ist Speicher das limitierende Element, mit nur 1 GB RAM kannst du nicht viele Threads haben). Ein moderner 64bit Rechner mit 4GB RAM sollte aber schon ein paar (zehn)Tausend hinbekommen. Mit 1 Thread pro CPU kannst du allerdings die Maximale auslastung ereichen (weniger und nicht alle CPU's sind abgedeckt, mehr und das wechseln zwischen Threads kostet performance). Gleichzeitig sorgt #Threads=#CPU dafür das die Threads nicht zwischendurch auf eine andere CPU geladen werden, was dafür sorgt das die Caches neu geladen werden müssen und daher Zugriffszeiten länger sind.