El modelo futuro de llamadas asincrónicas multiproceso
Cuando llamamos a una función, si la ejecución de la función tarda mucho, tenemos que esperar, pero a veces no tenemos prisa por obtener el resultado devuelto por la función. Por lo tanto, podemos hacer que el destinatario regrese inmediatamente y permitirle procesar lentamente la solicitud en segundo plano. Para la persona que llama, primero puede manejar algunas otras cosas y luego intentar obtener los datos cuando realmente los necesita (la ubicación donde realmente se necesitan los datos es el punto de bloqueo mencionado anteriormente). Esta es la idea central del patrón Future: llamadas asincrónicas.
En este punto, podrías estar pensando que CountDownLatch puede hacer cosas similares. También puede utilizar subprocesos para realizar tareas que requieren mucho tiempo y luego establecer un punto de bloqueo para esperar la devolución de los resultados. ¡Este parece ser el caso! Pero a veces se descubre que CountDownLatch solo conoce el estado de finalización del subproceso. Para recopilar resultados, también puede establecer el tiempo de espera del subproceso para evitar que la tarea principal espere para siempre.
Al ver esto, parece que CountDownLatch no tiene una buena idea de los resultados de ejecución del subproceso. Esta operación se puede completar usando Future. Entonces, ¿qué tiene de sagrado Future? Discutiremos esto en detalle a continuación.
Aunque el patrón Future no devuelve los datos que necesita inmediatamente, sí devuelve un contrato que puede utilizar para obtener los datos que necesita más adelante, cuando los necesite.
La figura anterior muestra el flujo de llamadas a programas en serie. Puede ver que cuando un programa se ejecuta de manera que requiere mucho tiempo, los otros programas tienen que esperar a que se complete la operación que requiere mucho tiempo, por lo que el cliente tiene que esperar a que regresen los datos antes de poder realizar el resto de la operación. tratamiento.
La figura anterior muestra el diagrama de flujo del patrón futuro. En el modelo Futuro generalizado, aunque obtener datos es una operación que requiere mucho tiempo, el programa de servicio no espera a que se completen los datos, sino que devuelve datos falsificados (es decir, el "contrato" mencionado anteriormente) al cliente, logrando así Modo futuro. El cliente no tiene prisa por procesarlo, pero primero procesa otros negocios y aprovecha al máximo el tiempo de espera. Este es el núcleo del modo Futuro. Después de completar otras tareas no relacionadas con los datos, finalmente devuelve los datos Futuro. una velocidad más lenta. Este es el núcleo del patrón Futuro. Después de completar otras tareas no relacionadas con datos, finalmente use un Future más lento para devolver los datos.
1. La función principal de Future
2. La estructura central de Future es la siguiente:
El proceso anterior significa: Los datos son la interfaz central, que es lo que el cliente quiere para obtener datos, en modo Futuro, esta interfaz de Datos tiene dos implementaciones importantes, a saber, RealData y FutureData. RealData son datos reales, y FutureData es una implementación de interfaz que se utiliza para extraer datos reales de RealData y se utiliza para devolverlos y obtenerlos de inmediato. En realidad, es un proxy para los datos reales de RealData, que encapsula el proceso de espera para obtener RealData.
Habiendo dicho tantas cosas teóricas, ¡veamos directamente el código!
Hay cinco clases principales, correspondientes a los roles principales en el patrón Futuro:
1. Interfaz de datos
2. Código
4. Código del cliente
5. Principal
6. Implementación del patrón, debido a que es un patrón muy común, el JDK también nos proporciona los métodos e interfaces correspondientes. Veamos un ejemplo:
Aquí RealData implementa la interfaz invocable, reescribe el método de llamada e implementa la operación que requiere mucho tiempo para construir RealData en el método de llamada.
Resultado:
El código anterior pasa: FutureTasklt; Stringgt; FutureTask = new FutureTasklt; new RealData("Hello"); El valor de retorno de esta tarea es de tipo String. Mire la relación del diagrama de clases de FutureTask a continuación:
FutureTask implementa la interfaz RunnableFuture, que hereda las interfaces Future y Runnable. Debido a que RunnableFuture implementa la interfaz Runnable, FutureTask se puede enviar al Ejecutor para su ejecución. FutureTask tiene dos métodos constructores, de la siguiente manera:
Constructor 1, el parámetro es invocable:
Constructor 2, el parámetro es ejecutable:
Capítulo Los dos constructores se pasan como interfaces Runnable y se convierten a Callable a través del método Executors.callable(). El proceso de adaptación es el siguiente:
¿Por qué es necesario convertir Runnable a Callable aquí? Primero, echemos un vistazo a la diferencia entre los dos:
El punto más crítico es el segundo punto, es decir, Callable tiene un valor de retorno, pero Runnable no. Callable proporciona métodos para verificar si el cálculo está completo, esperar a que se complete y obtener los resultados del cálculo.
Una vez completado el cálculo, solo podrás utilizar el método get para obtener el resultado. Si el hilo no ha terminado de ejecutarse, el método Future.get() puede bloquear la ejecución del hilo actual; si ocurre una excepción en el hilo, el método Future.get() arrojará InterruptedException o ExecutionException si el hilo ha sido ejecutado; cancelado, lanzará CancellationException.