Los amigos que han usado paquetes de concurrencia Java pueden estar familiarizados con el futuro (interfaz). El futuro incluso se admite directamente a nivel de sintaxis en algunos lenguajes de dominio (como Alice ML).
Aquí tomaremos java.util.concurrent.future como ejemplo para hablar brevemente sobre el método de trabajo específico del futuro. El objeto futuro en sí puede considerarse como una referencia explícita, una referencia a los resultados del procesamiento asincrónico. Debido a su naturaleza asincrónica, el objeto que hace referencia puede no estar disponible al comienzo de la creación (por ejemplo, todavía está en funcionamiento, transmisión de red o espera). En este momento, si el flujo del programa que se pone en el futuro no tiene prisa por usar el objeto que hace referencia al futuro, entonces puede hacer cualquier otra cosa que desee. ser dos situaciones:
Espero ver este objeto disponible y completar algunos procesos de seguimiento relacionados. Si realmente no está disponible, también puede ingresar otros procesos de ramificación.
"Sin ti, mi vida perderá su significado, así que incluso si el mar se ha ido, te esperaré". -tiempo)
Para el primer caso, puede determinar si el objeto referenciado está listo llamando a Future.IsDone () y tomar un procesamiento diferente;
Obtener (tiempo de espera, unidades de tiempo de tiempo de tiempo largas) espera a que el objeto esté listo mediante un bloqueo sincrónico. Si el tiempo de ejecución real se bloquea o devuelve inmediatamente depende del tiempo de llamada de get () y el orden del objeto listo.
En pocas palabras, el modo futuro puede satisfacer las necesidades de concurrencia basadas en datos en procesos continuos, no solo obtener mejoras de rendimiento en la ejecución concurrente, sino también la simplicidad y la elegancia de los procesos continuos.
Comparación con otros patrones de diseño concurrentes
Además del futuro, otros patrones de diseño de concurrencia comunes incluyen "Conducido de devolución de llamada (en entorno múltiple)", "Mensaje/Evento impulsado (en el modelo de actor)", etc.
La devolución de llamada es el modo de concurrencia asíncrono más común, que tiene una alta inmediatez y un diseño de interfaz simple. Pero en comparación con el futuro, sus desventajas también son muy obvias. Primero, las devoluciones de llamada en entornos multiproceso generalmente se ejecutan en el hilo del módulo que desencadena la devolución de llamada, lo que significa que los problemas de exclusión mutua de subprocesos deben considerarse al escribir métodos de devolución de llamada; Ejecutar devoluciones de llamada para aplicaciones de usuario, que también son relativamente inseguras, porque no puede determinar cuánto tiempo llevará o qué excepciones ocurrirá, lo que puede afectar indirectamente la inmediatez y confiabilidad de este módulo; secuencia. Por lo tanto, la interfaz de devolución de llamada es adecuada para escenarios en los que solo las tareas simples deben completarse en una devolución de llamada y no tiene que combinarse con otros procesos.
Las desventajas de los modos de devolución de llamada anteriores son exactamente las fortalezas del futuro. Dado que el uso de Future es incorporar naturalmente los controladores de datos asíncronos en procesos secuenciales, no tiene que considerar los problemas de hilos Mutex. ser mencionado a continuación "Futurez Future"). Por otro lado, los módulos que proporcionan interfaces futuras no tienen que preocuparse por problemas de confiabilidad como las interfaces de devolución de llamada y su posible impacto de inmediatez en este módulo.
Otro patrón de diseño concurrente común es el "mensaje (evento) impulsado", que generalmente se usa en el modelo de actor: el solicitante del servicio envía un mensaje al proveedor de servicios y luego continúa realizando tareas posteriores que no dependen de los resultados del procesamiento del servicio. y depende de ello si necesita confiar en él. Aunque este control concurrente basado en máquinas estatales es más adecuado para los procesos secuenciales de continuidad que las devoluciones de llamada, los desarrolladores tienen que reducir el proceso continuo en subprocesos múltiples específicos del estado de acuerdo con la llamada del servicio asincrónico, que aumenta artificialmente la complejidad del desarrollo. El uso del modo futuro puede evitar este problema y no tiene que romper los procesos continuos para las llamadas asincrónicas. Sin embargo, se debe tener en cuenta una cosa: el método Future.get () puede bloquear la ejecución de los subprocesos, por lo que generalmente no se puede incorporar directamente al modelo de actor convencional. (El modelo de actor con sede en Coroutine puede resolver mejor este conflicto)
La flexibilidad de Future también se refleja en su libre elección entre la sincronización y las elecciones asíncronas. las necesidades del proceso. Por ejemplo, puede decidir si usar esta brecha para completar otras tareas en función de si los datos están listos, lo cual es bastante conveniente para implementar el mecanismo de "predicción de rama asíncrona".
La derivada del futuro
Además de los formularios básicos mencionados anteriormente, el futuro también tiene cambios derivados ricos, por lo que aquí hay algunos comunes.
Futuro perezoso
A diferencia de los futuros generales, Lazy Future no comienza a preparar activamente el objeto referenciado al comienzo de la creación, pero espera hasta que se solicite el objeto antes de comenzar el trabajo correspondiente. Por lo tanto, el futuro perezoso en sí no tiene la intención de lograr la concurrencia, pero toma ahorrar recursos informáticos innecesarios como punto de partida, y su efecto es similar al Lambda/Cierre. Por ejemplo, al diseñar ciertas API, es posible que deba devolver un conjunto de información, y el cálculo de algunos de ellos puede consumir recursos considerables. Sin embargo, la persona que llama puede no preocuparse por toda esta información, por lo que proporcionar objetos que requieren más recursos en forma de futuro perezoso pueden ahorrar recursos cuando la persona que llama no necesita usar información específica.
Además, el Future Lazy también se puede utilizar para evitar exclusiones mutuas innecesarias causadas por la adquisición prematura o el bloqueo de los recursos.
Promesa
La promesa puede considerarse como una rama especial del futuro común. Pero la promesa se usa para representar explícitamente escenarios en los que los procesos asíncronos no son activados directamente por la persona que llama. Por ejemplo, el control de tiempo de la interfaz futura, su proceso asíncrono no es activado por la persona que llama, pero por el reloj del sistema. suscriptor, pero por el suscriptor. Por lo tanto, en comparación con el futuro estándar, la interfaz de promesa generalmente tiene una interfaz SET () o fulfill () adicional.
Futuro reutilizable
Un futuro regular es una vez, lo que significa que cuando obtienes resultados de procesamiento asincrónico, el objeto futuro en sí pierde su significado. Pero el futuro especialmente diseñado también se puede reutilizar, lo cual es muy útil para los datos que se pueden cambiar varias veces. Por ejemplo, la interfaz de estilo futuro proporcionado por el marco de suscripción distribuido Taobao mencionado anteriormente permite múltiples llamadas al método WaitNext () (equivalente a Future.get ()). Última llamada. La ventaja de este diseño es que el usuario de la interfaz puede responder a los cambios en los datos de suscripción en cualquier momento adecuado, o simplemente a través de un bucle infinito en un hilo independiente, al tiempo que tiene en cuenta otras tareas de tiempo, o incluso esperando múltiples tareas al mismo tiempo. Los ejemplos simplificados son los siguientes:
para (;;) {shitebe = getNextScheduledTaskTime (); .}} doscheduledTask ();} Uso del futuro
Primero, enumeremos un código en Java.
//: concurrencia/callableMo.javaimport java.util.concurrent.*; import java.util.*; classwithResult implementa Callable <String> {private int id; Call String () {return "Resultado de TaskWithResult" + ID; New ArrayList <Future <String> (); intentar : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : :::::::::::::::::::::::::::::::::::::::::::::::::::::: ::: ::::::::::::::::::::::::::::::::ort ; : Resultado de la tarea con tasksult 0 rescate de la tarea con tasksult 1Result of TaskwithResult 2Res Ultimate de TaskWithResult 3Result of TaskWithResult 4Result of TaskwithResult 5Result of TaskWithResult 6Result of TaskWithResult 7Result of TaskwithRessult 8Result de taskwithResult 9*Para explicar el uso de Future, primero, el método ExecutorService Object Exec. Puede usar el método isDone () para consultar si el futuro se ha completado. Cuando se completa la tarea, tiene un resultado y puede llamar al método get () para obtener el resultado. También puede llamar a Get () directamente sin la verificación de ISDone (). Puede llamar a la función get () con un tiempo de espera, o llamar a ISDone () primero para ver si la tarea se completa, y luego llamar a get ().