Javafx: ¿diferencia entre javafx.concurent y Platform.runLater?

Tengo curiosidad sobre cuál es exactamente la diferencia entre javafx.concurent y Platform.runLater para la progtwigción multiproceso de JavaFx.

¿Significa que con javafx.concurrent, podemos tener múltiples hilos de dibujo reales o todo termina en un hilo de todos modos?

Por ejemplo, una cosa que disfruté fue usar JavafX y swing al mismo tiempo, ya que ambos usaron 2 hilos de dibujo diferentes. Utilizaría el swing para cosas pesadas (por ejemplo, abrir un FileChooser) y usar JavaFX para el material visual central, por ejemplo, reproducir un video continuo y en bucle. Sin embargo, el Mac lo hace imposible debido a ese error de excepción sin cabeza, por lo que todo cayó en javafx y eso significó muchas pausas cuando abría cosas como abrir los filechoosers.

Si reescribo mi aplicación con javafx.concurrent, ¿podría esencialmente imitar esa experiencia de 2 draw threads como lo hice una vez con Swing + JavaFX?

Platform.runLater

Un Worker es complementario de Platform.runLater .

  • Utilice Platform.runLater cuando esté ejecutando fuera del subproceso de la aplicación JavaFX y desee ejecutar un poco de lógica en el subproceso de la aplicación JavaFX.
  • Use un Worker cuando esté ejecutando en el subproceso de la aplicación JavaFX y desee generar alguna lógica o (especialmente) E / S en un nuevo subproceso para que no bloquee el subproceso de la aplicación JavaFX.

Nunca querría hacer E / S de red dentro del método de run Platform.runLater , pero a menudo querría hacerlo en un método de call Worker .

Tarea y servicio

Considere usar las subclases de Tarea o Servicio de Trabajador . Estos son envoltorios JavaFX para FutureTask (que a su vez es Runnable ). Los trabajadores proporcionan un método de llamada para ejecutar la lógica en un hilo de fondo. Mantienen el estado de ejecución (con notificación de callback segura a la hebra JavaFX para cambios de estado) y devuelven resultados de la llamada a través de propiedades de valor , mensaje y excepción .

Aproveche los patrones de diseño en los ejemplos de javadoc de Task y Service para simplificar la creación de aplicaciones seguras para subprocesos con funciones como:

  • Captura asincrónica de datos para la actualización de UI.
  • Actualizaciones periódicas de mensajes para el progreso de la tarea.
  • Construir gráficos de nodo que aún no están unidos a una escena mostrada.
  • Monitoreando el progreso a través de barras de progreso, etc.

Utilizando Workers y Platform.runLater together

Además, el uso de Task y Service no es incompatible con el uso de Platform.runLater . Por ejemplo, si tiene una Task ejecución muy larga desde la cual desea devolver resultados parciales a la IU periódicamente o cuando se llena un búfer, ejecutar Platform.runLater en el método de call la tarea es la forma de hacerlo.

Trabajando con sistemas de roscado existentes

Los trabajadores son útiles cuando no tiene un servicio de subprocesamiento existente provisto por una biblioteca, sino que crean sus propios subprocesos para ejecutarlos en segundo plano. Si tiene un servicio de subprocesamiento existente, necesitará usar Platform.runLater para realizar la lógica en el subproceso de la aplicación JavaFX.

Tenga cuidado al escribir código multihilo

Tenga en cuenta que aún necesita saber lo que está haciendo, incluso si usa un Worker . También debe tener cuidado de no infringir las reglas de concurrencia JavaFX estándar, como no actualizar nunca los nodos en el gráfico de escena activo (incluso no actualizar los valores a los que están vinculados los nodos en el gráfico de escena activo, como la lista observable de elementos de respaldo). un ListView ).

Respondiendo algunas de sus preguntas adicionales

¿Significa que con javafx.concurrent, podemos tener múltiples hilos de dibujo reales o todo termina en un hilo de todos modos?

Solo hay un hilo de renderizado en JavaFX. No puede hacer más subprocesos de representación utilizando JavaFX concurrente. Puede hacer cosas como crear nodos fuera del subproceso JavaFX o establecer píxeles en una pantalla WriteableImage o Canvas sin utilizar muchos subprocesos, pero finalmente cada operación de renderización pasa a través de un solo subproceso administrado por el sistema JavaFX sobre el que no tiene control.

Si reescribo mi aplicación con javafx.concurrent, ¿podría esencialmente imitar esa experiencia de 2 draw threads como lo hice una vez con Swing + JavaFX?

No. Incluso si pudieras, no creo que sea aconsejable. Con un modelo así es demasiado fácil crear errores sutiles y difíciles de depurar relacionados con el procesamiento de subprocesos. Y la ganancia que puede obtener de dicha configuración puede ser menor de lo que pueda desear o esperar.

Ver artículos relacionados sobre por qué no es aconsejable tener 2 o más “draw threads”:

  • Threadaches
  • Juegos de herramientas multiproceso: un sueño fallido

Java 8 está agregando un conmutador de línea de comando experimental para usar el mismo subproceso para el subproceso de la aplicación JavaFX y el subproceso de despacho del evento Swing. La razón principal de esto es que simplifica el modelo de progtwigción.

todo cayó en javafx y eso significó muchas pausas al hacer cosas como abrir filechoosers.

Tal vez hubo ineficiencias en su código (como la realización de E / S en el hilo de la interfaz de usuario) que causó las pausas.

las cosas pesadas (por ejemplo, abrir un FileChooser)

Abrir y renderizar FileChooser no es pesado. JavaFX puede manejar fácilmente una operación de este tipo sin una degradación del rendimiento. Lo que puede consumir mucho tiempo está relacionado con las E / S, por ejemplo, recorrer un árbol grande de archivos recursivamente para obtener atributos de archivos. Lo que puede hacer en esos casos es engendrar un hilo para que E / S lo ejecute en un Worker y periódicamente retroalimentar resultados parciales al thread de UI a través de Platform.runLater . Tal esquema funcionará bien. El cuello de botella no es el dibujo, por lo que tener otro hilo de dibujo no proporciona ninguna ventaja. El cuello de botella es el sistema lento de E / S y este cuello de botella lo mitalea usando una hebra separada para E / S para que el hilo principal de UI no se vea afectado y el usuario no experimente lockings de IU mientras se está produciendo E / S.