Red de conocimiento informático - Conocimiento sistemático - ¡Impresionante! Artefacto asincrónico: principio de implementación de CompletableFuture y escenarios de uso

¡Impresionante! Artefacto asincrónico: principio de implementación de CompletableFuture y escenarios de uso

CompletableFuture es una clase de implementación introducida en jdk1.8. Extended Future y CompletionStage, es un futuro que puede desencadenar algunas operaciones durante la fase de finalización de la tarea. En pocas palabras, se pueden implementar devoluciones de llamada asincrónicas.

Para el futuro de jdk1.5, aunque proporciona la capacidad de procesar tareas de forma asincrónica, la forma de obtener los resultados no es elegante y aún requiere bloqueo (o entrenamiento de rotación). ¿Cómo evitar el bloqueo? De hecho, es para registrar una devolución de llamada.

La industria combina el patrón de observador para implementar devoluciones de llamadas asincrónicas. Es decir, notificar al observador cuando se complete la ejecución de la tarea. Por ejemplo, ChannelFuture de Netty puede procesar resultados asincrónicos registrando un oyente.

Registra el oyente mediante el método addListener. Si se completa la tarea, se llamará a la notificación notifyListeners.

CompletableFuture extiende Future, introduce programación funcional y procesa resultados a través de devoluciones de llamada.

La función de CompletableFuture se refleja principalmente en su CompletionStage.

Se pueden implementar las siguientes funciones

La diferencia entre consumo y operación:

El consumo utiliza los resultados de la ejecución. Ejecutar simplemente ejecuta una tarea específica. Puede comprobar las otras funciones específicas usted mismo según sus necesidades.

Aquí tienes un ejemplo sencillo para experimentar su funcionalidad.

Resultados de la ejecución

Según los resultados, podemos ver que las tareas correspondientes se ejecutarán de manera ordenada.

Nota:

Aquí hay una descripción, si hay varias tareas dependientes para la misma tarea:

La conclusión anterior se obtiene leyendo el código fuente. Profundicemos en el código fuente.

Hay muchas formas de crearlo, e incluso puedes crear uno nuevo directamente. Echemos un vistazo al método de creación asincrónica de SupplyAsync.

Parámetro de entrada Proveedor, función con valor de retorno. Si es un método asincrónico y se pasa un ejecutor, el ejecutor pasado se utilizará para ejecutar la tarea. De lo contrario, utilice el grupo de subprocesos paralelos público de ForkJoin. Si no se admite el paralelismo, cree un nuevo subproceso para la ejecución.

Aquí debemos tener en cuenta que ForkJoin realiza tareas a través de subprocesos de demonio. Entonces debe haber un hilo que no sea demonio.

Esto creará un CompletableFuture para la devolución.

Luego construya un AsyncSupply y pase el CompletableFuture creado como parámetro de construcción.

Entonces, la ejecución de la tarea depende completamente de AsyncSupply.

Antes de ver el método postComplete, veamos primero la lógica de crear tareas dependientes.

Mencionado anteriormente. LuegoAcceptAsync se usa para consumir CompletableFuture. Este método llama a uniAcceptStage.

Lógica uniAcceptStage:

Lógica Mark1:

Permítanme hablar brevemente aquí, de hecho, el modo tiene síncrono, asíncrono e iteración. Iterar para evitar la recursividad infinita.

Aquí destacamos el tercer parámetro del método d.uniAccept.

Si es una llamada asincrónica (modo>0), pase nulo. De lo contrario pasa esto.

Consulta el código a continuación para ver la diferencia. Si c no es nulo, se llamará al método c.claim.

El método de reclamo es lógico:

La tarea de ejecución de esto es la siguiente. Es decir, el método tryFire se llama sincrónicamente en un hilo asincrónico. Cumpliendo su propósito de ser ejecutado por un hilo asíncrono.

Después de leer la lógica anterior, básicamente entendemos la lógica de las tareas dependientes.

De hecho, primero es determinar si la tarea de origen se completa. Si se completa, la tarea anterior se ejecutará directamente en el hilo correspondiente (si es sincrónico, se procesará en el. hilo actual, de lo contrario se procesará en el hilo asincrónico)

Si la tarea no se completa, regrese directamente, porque una vez completada la tarea, postComplete se usará para activar la llamada a la tarea dependiente.

Se llama después de que se haya completado la tarea de origen.

De hecho, la lógica es muy simple: iterar las tareas dependientes de la pila. Llame al método h.tryFire. NESTED es para evitar bucles infinitos recursivos. Porque FirePost llamará a postComplete. Si está NESTED, no se llama.

El contenido de la pila en realidad se agrega cuando se crea la tarea dependiente. Ya lo mencionamos arriba.

Básicamente se ha analizado la lógica del código fuente anterior.

Debido a que implica operaciones asincrónicas y de otro tipo, debemos ocuparnos de ello (aquí para tareas totalmente asincrónicas):

Considere principalmente la reutilización de código. Entonces la lógica es relativamente difícil de entender.

El método postComplete se llamará después de que el hilo de la tarea de origen termine de ejecutar la tarea de origen. También se le puede llamar después de un hilo de tarea dependiente.

La forma principal de realizar tareas dependientes es confiar en el método tryFire. Debido a que este método puede ser activado por muchos tipos diferentes de subprocesos, la lógica es un poco complicada. (Otros subprocesos de tareas dependientes, subprocesos de tareas de origen, subprocesos de tareas dependientes actuales)

Debo decir que la codificación de Doug Lea es realmente un arte. La reutilización del código ahora es lógica.