El marco de la horquilla/unión es una implementación de la interfaz EjecutorService, a través de la cual podemos implementar múltiples procesos. La horquilla/unión se puede usar para dividir recursivamente una tarea grande en múltiples tareas pequeñas, con el objetivo de hacer uso completo de todos los recursos para mejorar el rendimiento de la aplicación tanto como sea posible.
Al igual que cualquier implementación de la interfaz de EjecutorService, Fork/Join también utiliza grupos de subprocesos para administrar los hilos de trabajadores distribuidos. Lo único del marco de la horquilla/unión es que utiliza el algoritmo de robo de trabajo. A través de este algoritmo, los hilos de trabajadores pueden robar otras tareas de hilo ocupados para ejecutar cuando no se puede hacer nada.
El núcleo del marco de la bifurcación/unión es la clase Forkjoinpool, una subclase de la clase AbstractExecutorService. Forkjoinpool implementa el algoritmo central de robo de trabajo y puede realizar el procesamiento de ForkJoinkask.
Uso básico
El primer paso en el uso del marco de la horquilla/unión es escribir un código que realice tareas fragmentadas. El código a escribir es similar al siguiente código de pseudo:
Si la tarea es lo suficientemente pequeña: ejecute la tarea directamente más: corte la tarea en dos tareas pequeñas para ejecutar dos tareas pequeñas y esperar el resultado
Use la subclase ForkJoTask para encapsular el código como se indicó anteriormente. Por lo general, se utilizan algunas clases proporcionadas por JDK, incluida RecursivetAk (esta clase devolverá un resultado) y recursIveAction.
Después de preparar la subclase ForkJoTask, cree un objeto que represente todas las tareas y pase a un método Invoke () de una instancia de ForkjoInpool.
De desenfoque a claro
Para ayudar a comprender cómo funciona el marco de la bifurcación/unión, utilizamos un caso para ilustrar: por ejemplo, difuminar una imagen. Representamos la imagen usando una matriz entera, donde cada valor numérico representa el color de un píxel. La imagen borrosa también está representada por una matriz de la misma longitud.
La ejecución de desenfoque se logra procesando cada píxel que representa la imagen. Calcule la media de cada píxel y sus píxeles circundantes (la media de los tres colores primarios de rojo, amarillo y azul), y la matriz de resultados resultante es la imagen borrosa. Dado que la representación de imágenes suele ser una gran matriz, todo el proceso generalmente lleva mucho tiempo. El marco de la horquilla/unión se puede utilizar para aprovechar las ventajas del procesamiento concurrente en los sistemas multiprocesador para acelerar. Aquí hay una posible implementación:
paquete com.zhyea.robin; import java.util.concurrent.recursiveaction; Class Public Forkblur extiende recursIVeaction {private int [] msource; private int mStart; Private int mlength; private int [] mDestination; // Manejo del tamaño de la ventana; Necesita ser un número impar. privado int mblurWidth = 15; public forkblur (int [] src, int start, int long, int [] dst) {msource = src; mStart = inicio; mlength = longitud; mDestination = dst; } protegido void computedirectly () {int SidePixels = (mblurWidth - 1) / 2; for (int index = mStart; index <mStart+mLength; index ++) {// Calcule el valor promedio. flotante rt = 0, gt = 0, bt = 0; for (int mi = -sidePixels; mi <= sidePixels; mi ++) {int mindEx = math.min (math.max (mi+index, 0), msource.length - 1); int pixel = mSource [MindEx]; rt += (float) ((píxel y 0x00ff0000) >> 16) / mblurWidth; gt += (float) ((píxel y 0x00000ff00) >> 8) / mblurwidth; bt += (float) ((píxel y 0x000000ff) >> 0) / mblurWidth; } // Reorganizar el píxel objetivo. int dpixel = (0xff0000000) | (((int) rt) << 16) | (((int) gt) << 8) | (((int) bt) << 0); mDestination [índice] = dpixel; }} ....}Ahora implementa el método abstracto Compute (), en el que se implementan ambas operaciones difusas, y se implementa dividir una tarea en dos pequeñas tareas. Aquí simplemente decidimos si ejecutar la tarea directamente o dividirla en dos pequeñas tareas en función de la longitud de la matriz:
protegido static int Sthreshold = 100000; protegido vacío () {if (mlength <sthreshold) {computedirectly (); devolver; } int split = mlength / 2; InvokeAll (nuevo Forkblur (MSource, MStart, Split, MDestination), New Forkblur (MSource, MStart + Splitch, MLength - Split, MDestination)); }Debido a que la implementación de los métodos anteriores se define en una subclase de recursiva, las tareas se pueden crear y ejecutar directamente en un biforzado. Los pasos específicos son los siguientes:
1. Cree un objeto que represente la tarea a realizar:
// Src representa una matriz de píxeles de la imagen de origen // dst representa los píxeles de la imagen generada forkblur fb = new Forkblur (Src, 0, src.length, dst);
2. Cree una instancia de ForkjoInpool que ejecute la tarea:
ForkJoinPool pool = new ForkJoinPool();
3. Ejecute la tarea:
pool.invoke(fb);
El código fuente también contiene algún código para crear la imagen de destino. Para más detalles, consulte el ejemplo de Forkblur.
Implementación estándar
Para usar el marco de la horquilla/unión para ejecutar tareas concurrentes en sistemas de múltiples núcleos de acuerdo con los algoritmos personalizados, por supuesto, debe implementar clases personalizadas (como la clase Forkblur que implementamos antes). Además, algunas características del marco de la horquilla/unión se han utilizado ampliamente en Javase. Por ejemplo, el método ParallelSort () de la clase Java.util.Arrays en Java8 usa el marco de la bifurcación/unión. Para más detalles, consulte la documentación de la API Java.
Otra implementación del marco de la bifurcación/unión está debajo del paquete Java.util.streams, que también forma parte de la función Lambda de Java8.