Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Aprende Forkjoinpool | Mejores Prácticas de Multithreading
Multithreading en Java

bookForkjoinpool

La clase ForkJoinPool en Java para trabajar con el framework Fork/Join es precisamente la realización de esto. Proporciona mecanismos para gestionar tareas que pueden dividirse en subtareas más pequeñas y ejecutarse en paralelo.

Main.java

Main.java

copy
1
ForkJoinPool forkJoinPool = new ForkJoinPool();

ForkJoinPool se basa en el concepto de dos acciones básicas: fork y join. Fork es una acción en la que se divide una tarea grande en varias subtareas más pequeñas que pueden ejecutarse en paralelo. Join es el proceso mediante el cual los resultados de estas subtareas se combinan para formar el resultado de la tarea original.

Estudio de caso

Suponga que necesita analizar un conjunto de datos enorme que puede dividirse en varios conjuntos más pequeños. Si procesa cada conjunto de datos por separado y luego fusiona los resultados, puede acelerar el procesamiento de manera significativa, especialmente en sistemas multiprocesador.

Cómo utilizar ForkJoinPool

Existen 2 clases para implementar tareas en ellas, que son RecursiveTask y RecursiveAction. Ambas requieren que se implemente el método abstracto compute(). En RecursiveTask, el método compute() devuelve un valor, mientras que en RecursiveAction el método compute() devuelve void.

Main.java

Main.java

copy
12345678
class ExampleTask extends RecursiveTask<String> { @Override protected String compute() { // code return null; } }

Cuando heredamos de RecursiveTask, debemos asegurarnos de especificar qué tipo de dato devolverá nuestro método compute() utilizando esta sintaxis RecursiveTask<String>.

Main.java

Main.java

copy
1234567
class ExampleAction extends RecursiveAction { @Override protected void compute() { // code } }

Aquí es lo contrario, no es necesario especificar el tipo explícitamente en RecursiveAction, porque nuestro método no devolverá nada y solo realizará la tarea.

Inicio de una tarea

Por cierto, podemos iniciar una tarea para su ejecución sin siquiera usar ForkJoinPool, simplemente utilizando los métodos fork() y join().

Es importante notar que el método fork() envía la tarea a algún hilo. El método join() se utiliza para obtener el resultado.

Main.java

Main.java

copy
12345
public static void main(String[] args) { ExampleTask simpleClass = new ExampleTask(); simpleClass.fork(); System.out.println(simpleClass.join()); }

Métodos principales de ForkJoinPool

  • invoke(): Inicia una tarea en el pool y espera a que se complete. Devuelve el resultado de la finalización de la tarea;
  • submit(): Envía una tarea al pool, pero no bloquea el hilo actual mientras espera que la tarea se complete. Puede utilizarse para enviar tareas y recuperar sus objetos Future;
  • execute(): Ejecuta una tarea en el pool, pero no devuelve un resultado y no bloquea el hilo actual.

¿Cómo podemos iniciar una tarea usando ForkJoinPool,muy sencillo!

Main.java

Main.java

copy
12345
public static void main(String[] args) { ExampleAction simpleClass = new ExampleAction(); ForkJoinPool forkJoinPool = new ForkJoinPool(); System.out.println(forkJoinPool.invoke(simpleClass)); }

¿Pero cuál es la diferencia entre ejecutar fork(), join() y mediante la clase ForkJoinPool?

Es muy sencillo. Cuando se utiliza el primer método con la ayuda de los métodos fork(), join(), la tarea se inicia en el mismo hilo en el que se llamaron estos métodos, bloqueando este hilo, mientras que con la ayuda de la clase ForkJoinPool se asigna un hilo del pool y este trabaja en ese hilo sin bloquear el hilo principal.

Analicemos más de cerca, supongamos que tenemos una implementación así:

Main.java

Main.java

copy
12345678
class ExampleRecursiveTask extends RecursiveTask<String> { @Override protected String compute() { System.out.println("Thread: " + Thread.currentThread().getName()); return "Wow, it works!!!"; } }

Y queremos ejecutar este código utilizando fork() y join(). Veamos qué se imprimirá en la consola y qué hilo realizará esta tarea.

Main.java

Main.java

copy
1234567
public class Main { public static void main(String[] args) { ExampleRecursiveTask simpleClass = new ExampleRecursiveTask(); simpleClass.fork(); System.out.println(simpleClass.join()); } }

Y obtenemos esta salida en la consola:

Thread: main
Wow, it works!!!

Ahora veamos qué sucede si ejecutamos con ForkJoinPool:

Main.java

Main.java

copy
1234567
public class Main { public static void main(String[] args) { ExampleRecursiveTask simpleClass = new ExampleRecursiveTask(); ForkJoinPool forkJoinPool = new ForkJoinPool(); System.out.println(forkJoinPool.invoke(simpleClass)); } }

Y obtenemos la siguiente conclusión:

Thread: ForkJoinPool-1-worker-1
Wow, it works!!!

Como se puede observar, la diferencia es que utilizando el primer método la tarea se ejecuta en el mismo hilo que la invocó (hilo principal). Sin embargo, si se utiliza el segundo método, el hilo se toma del grupo de hilos ForkJoinPool y el hilo principal que llamó a esta lógica no se bloquea y continúa.

Cómo implementar Fork/Join en código

La manera más sencilla de explicar esto sería mediante un video, en lugar de proporcionarte 50-80 líneas de código y explicarlo punto por punto.

Note
Nota

ForkJoinPool es eficaz para tareas que pueden dividirse fácilmente en subtareas más pequeñas. Sin embargo, si las tareas son demasiado pequeñas, el uso de ForkJoinPool puede no aportar una mejora significativa en el rendimiento.

¿Todo estuvo claro?

¿Cómo podemos mejorarlo?

¡Gracias por tus comentarios!

Sección 4. Capítulo 2

Pregunte a AI

expand

Pregunte a AI

ChatGPT

Pregunte lo que quiera o pruebe una de las preguntas sugeridas para comenzar nuestra charla

Awesome!

Completion rate improved to 3.33

bookForkjoinpool

Desliza para mostrar el menú

La clase ForkJoinPool en Java para trabajar con el framework Fork/Join es precisamente la realización de esto. Proporciona mecanismos para gestionar tareas que pueden dividirse en subtareas más pequeñas y ejecutarse en paralelo.

Main.java

Main.java

copy
1
ForkJoinPool forkJoinPool = new ForkJoinPool();

ForkJoinPool se basa en el concepto de dos acciones básicas: fork y join. Fork es una acción en la que se divide una tarea grande en varias subtareas más pequeñas que pueden ejecutarse en paralelo. Join es el proceso mediante el cual los resultados de estas subtareas se combinan para formar el resultado de la tarea original.

Estudio de caso

Suponga que necesita analizar un conjunto de datos enorme que puede dividirse en varios conjuntos más pequeños. Si procesa cada conjunto de datos por separado y luego fusiona los resultados, puede acelerar el procesamiento de manera significativa, especialmente en sistemas multiprocesador.

Cómo utilizar ForkJoinPool

Existen 2 clases para implementar tareas en ellas, que son RecursiveTask y RecursiveAction. Ambas requieren que se implemente el método abstracto compute(). En RecursiveTask, el método compute() devuelve un valor, mientras que en RecursiveAction el método compute() devuelve void.

Main.java

Main.java

copy
12345678
class ExampleTask extends RecursiveTask<String> { @Override protected String compute() { // code return null; } }

Cuando heredamos de RecursiveTask, debemos asegurarnos de especificar qué tipo de dato devolverá nuestro método compute() utilizando esta sintaxis RecursiveTask<String>.

Main.java

Main.java

copy
1234567
class ExampleAction extends RecursiveAction { @Override protected void compute() { // code } }

Aquí es lo contrario, no es necesario especificar el tipo explícitamente en RecursiveAction, porque nuestro método no devolverá nada y solo realizará la tarea.

Inicio de una tarea

Por cierto, podemos iniciar una tarea para su ejecución sin siquiera usar ForkJoinPool, simplemente utilizando los métodos fork() y join().

Es importante notar que el método fork() envía la tarea a algún hilo. El método join() se utiliza para obtener el resultado.

Main.java

Main.java

copy
12345
public static void main(String[] args) { ExampleTask simpleClass = new ExampleTask(); simpleClass.fork(); System.out.println(simpleClass.join()); }

Métodos principales de ForkJoinPool

  • invoke(): Inicia una tarea en el pool y espera a que se complete. Devuelve el resultado de la finalización de la tarea;
  • submit(): Envía una tarea al pool, pero no bloquea el hilo actual mientras espera que la tarea se complete. Puede utilizarse para enviar tareas y recuperar sus objetos Future;
  • execute(): Ejecuta una tarea en el pool, pero no devuelve un resultado y no bloquea el hilo actual.

¿Cómo podemos iniciar una tarea usando ForkJoinPool,muy sencillo!

Main.java

Main.java

copy
12345
public static void main(String[] args) { ExampleAction simpleClass = new ExampleAction(); ForkJoinPool forkJoinPool = new ForkJoinPool(); System.out.println(forkJoinPool.invoke(simpleClass)); }

¿Pero cuál es la diferencia entre ejecutar fork(), join() y mediante la clase ForkJoinPool?

Es muy sencillo. Cuando se utiliza el primer método con la ayuda de los métodos fork(), join(), la tarea se inicia en el mismo hilo en el que se llamaron estos métodos, bloqueando este hilo, mientras que con la ayuda de la clase ForkJoinPool se asigna un hilo del pool y este trabaja en ese hilo sin bloquear el hilo principal.

Analicemos más de cerca, supongamos que tenemos una implementación así:

Main.java

Main.java

copy
12345678
class ExampleRecursiveTask extends RecursiveTask<String> { @Override protected String compute() { System.out.println("Thread: " + Thread.currentThread().getName()); return "Wow, it works!!!"; } }

Y queremos ejecutar este código utilizando fork() y join(). Veamos qué se imprimirá en la consola y qué hilo realizará esta tarea.

Main.java

Main.java

copy
1234567
public class Main { public static void main(String[] args) { ExampleRecursiveTask simpleClass = new ExampleRecursiveTask(); simpleClass.fork(); System.out.println(simpleClass.join()); } }

Y obtenemos esta salida en la consola:

Thread: main
Wow, it works!!!

Ahora veamos qué sucede si ejecutamos con ForkJoinPool:

Main.java

Main.java

copy
1234567
public class Main { public static void main(String[] args) { ExampleRecursiveTask simpleClass = new ExampleRecursiveTask(); ForkJoinPool forkJoinPool = new ForkJoinPool(); System.out.println(forkJoinPool.invoke(simpleClass)); } }

Y obtenemos la siguiente conclusión:

Thread: ForkJoinPool-1-worker-1
Wow, it works!!!

Como se puede observar, la diferencia es que utilizando el primer método la tarea se ejecuta en el mismo hilo que la invocó (hilo principal). Sin embargo, si se utiliza el segundo método, el hilo se toma del grupo de hilos ForkJoinPool y el hilo principal que llamó a esta lógica no se bloquea y continúa.

Cómo implementar Fork/Join en código

La manera más sencilla de explicar esto sería mediante un video, en lugar de proporcionarte 50-80 líneas de código y explicarlo punto por punto.

Note
Nota

ForkJoinPool es eficaz para tareas que pueden dividirse fácilmente en subtareas más pequeñas. Sin embargo, si las tareas son demasiado pequeñas, el uso de ForkJoinPool puede no aportar una mejora significativa en el rendimiento.

¿Todo estuvo claro?

¿Cómo podemos mejorarlo?

¡Gracias por tus comentarios!

Sección 4. Capítulo 2
some-alt