framework - java gui
IO no bloqueante vs IO asincrónico e implementación en Java (4)
Tratando de resumir por mí mismo la diferencia entre estos 2 conceptos (porque estoy realmente confundido cuando veo que las personas los utilizan en una oración, como "no-blocking async IO", que estoy tratando de deducir de qué se trata). media).
Entonces, en mi entender, IO no bloqueante es el mecanismo principal del sistema operativo para procesar el IO si hay datos listos, de lo contrario simplemente devuelve el error / no hace nada.
En async IO solo proporciona una devolución de llamada, y su aplicación recibirá una notificación cuando los datos estén disponibles.
Entonces, ¿qué es en realidad "no-blocking async IO"? Y cómo todos ellos se pueden implementar en Java (JDK estándar, sin libretas externas, sé que hay java.nio.channels.{Channels, Selector, SelectorKey}
y java.nio.channels.{AsynchronousSocketChannel}
): IO no bloqueante , async IO y no-blocking async IO (si existe tal cosa)?
Entonces, ¿qué es en realidad "no-blocking async IO"?
Para responder eso, primero debes entender que no existe el bloqueo de la E / S asíncrona . El mismo concepto de asincronismo dicta que no hay espera, ni bloqueo, ni demora. Cuando ve E / S asíncrona sin bloqueo, el bit sin bloqueo solo sirve para calificar aún más el adjetivo asíncrono en ese término. De manera efectiva, la E / S asincrónica sin bloqueo podría ser un poco redundante.
Hay principalmente dos tipos de E / S. Sincrónico y asincrónico . Synchronous bloquea el hilo de ejecución actual hasta que se completa el proceso , mientras que Asynchronous no bloquea el hilo de ejecución actual, sino que pasa el control al kernel del sistema operativo para su posterior procesamiento. El kernel luego aconseja el subproceso asincrónico cuando la tarea enviada se completa
Grupos de canales asincrónicos
El concepto de canales asíncronos en java está respaldado por grupos de canales asíncronos. Un grupo de canales asíncronos agrupa básicamente una cantidad de canales para su reutilización. Los consumidores de la API asíncrona recuperan un canal del grupo (la JVM lo crea de forma predeterminada) y el canal se ubica automáticamente en el grupo una vez que ha completado su operación de lectura / escritura. En definitiva, los grupos de canales Async están respaldados por grupos de hilos sorpresa . Además, los canales asíncronos son seguros para el hilo.
El tamaño del grupo de subprocesos que respalda un grupo de canales asíncronos se configura mediante la siguiente propiedad de JVM
java.nio.channels.DefaultThreadPool.initialSize
que, dado un valor entero, configurará un grupo de subprocesos de ese tamaño para respaldar el grupo de canales. El grupo de canales se crea y mantiene de forma transparente para el desarrollador de lo contrario.
Y cómo todos ellos pueden implementarse en Java
Bueno, me alegro de que hayas preguntado. Aquí hay un ejemplo de un AsynchronousSocketChannel
(utilizado para abrir un Socket
cliente no bloqueante a un servidor de escucha). Este ejemplo es un extracto de Apress Pro Java NIO.2 , comentado por mí:
//Create an Asynchronous channel. No connection has actually been established yet
AsynchronousSocketChannel asynchronousSocketChannel = AsynchronousSocketChannel.open();
/**Connect to an actual server on the given port and address.
The operation returns a type of Future, the basis of the all
asynchronous operations in java. In this case, a Void is
returned because nothing is returned after a successful socket connection
*/
Void connect = asynchronousSocketChannel.connect(new InetSocketAddress("127.0.0.1", 5000)).get();
//Allocate data structures to use to communicate over the wire
ByteBuffer helloBuffer = ByteBuffer.wrap("Hello !".getBytes());
//Send the message
Future<Integer> successfullyWritten= asynchronousSocketChannel.write(helloBuffer);
//Do some stuff here. The point here is that asynchronousSocketChannel.write()
//returns almost immediately, not waiting to actually finish writing
//the hello to the channel before returning control to the currently executing thread
doSomethingElse();
//now you can come back and check if it was all written (or not)
System.out.println("Bytes written "+successfullyWritten.get());
EDIT: Debo mencionar que el soporte para Async NIO vino en JDK 1.7
Veo que esta es una vieja pregunta, pero creo que algo se perdió aquí, que @nickdu intentó señalar, pero no estaba del todo claro.
Hay cuatro tipos de IO pertinentes para esta discusión:
Bloqueando IO
IO sin bloqueo
IO asíncrono
Asincrónico sin bloqueo IO
La confusión surge, creo, debido a definiciones ambiguas. Así que déjame intentar aclarar eso.
Primero, hablemos de IO. Cuando tenemos IO lento, esto es más evidente, pero las operaciones de IO pueden ser de bloqueo o de no bloqueo. Esto no tiene nada que ver con los hilos, tiene que ver con la interfaz del sistema operativo. Cuando solicito al OS una operación de IO, tengo la opción de esperar a que todos los datos estén listos ( bloqueo ) u obtener lo que está disponible ahora y continuar ( no bloqueante ). El valor predeterminado es bloquear IO. Es mucho más fácil escribir código usando IO de bloqueo ya que la ruta es mucho más clara. Sin embargo, su código debe detenerse y esperar a que IO lo complete. El IO sin bloqueo requiere una interfaz con las librerías IO en un nivel inferior, usando select y read / write en lugar de las bibliotecas de nivel superior que proporcionan operaciones convenientes. IO sin bloqueo también implica que tienes algo en lo que necesitas trabajar mientras el sistema operativo trabaja en la IO. Esto podría ser múltiples operaciones de IO o computación en el IO que se ha completado.
Bloqueo de IO : la aplicación espera que el sistema operativo recopile todos los bytes para completar la operación o llegar al final antes de continuar. Esto es predeterminado. Para ser más claro para los muy técnicos, la llamada al sistema que inicia el IO instalará un controlador de señal esperando una interrupción del procesador que ocurrirá cuando la operación IO progrese. Luego, la llamada al sistema comenzará una suspensión que suspenderá la operación del proceso actual durante un período de tiempo, o hasta que ocurra la interrupción del proceso.
IO sin bloqueo : la aplicación le dice al sistema operativo que solo quiere qué bytes están disponibles en este momento, y se mueve mientras el sistema operativo recopila más bytes. El código usa select para determinar qué operaciones de IO tienen bytes disponibles. En este caso, la llamada al sistema volverá a instalar un manejador de señal, pero en lugar de reposar, asociará el manejador de señal con el manejador de archivo e inmediatamente regresará. El proceso será responsable de verificar periódicamente el identificador del archivo para el indicador de interrupción que se ha establecido. Esto generalmente se hace con una llamada de selección.
Ahora asincrónico es donde comienza la confusión. El concepto general de asíncrono solo implica que el proceso continúa mientras se realiza la operación en segundo plano, el mecanismo por el cual esto ocurre no es específico. El término es ambiguo ya que tanto IO no bloqueante como IO de bloqueo con hilos pueden considerarse asincrónicos. Ambos permiten operaciones simultáneas, sin embargo, los requisitos de recursos son diferentes y el código es sustancialmente diferente. Debido a que ha hecho una pregunta "¿Qué es IO asíncrono sin bloqueo?", Voy a utilizar una definición más estricta para un sistema asíncrono, con enhebrado, que realiza IO que puede o no ser no bloqueante.
La definición general
IO asíncrono: IO programático que permite que ocurran múltiples operaciones simultáneas de IO. Las operaciones de IO están sucediendo simultáneamente, por lo que el código no está esperando datos que no están listos.
La definición más estricta
IO asíncrono: IO programático que utiliza subprocesamiento o multiprocesamiento para permitir que se produzcan operaciones de E / S simultáneas.
Ahora, con esas definiciones más claras, tenemos los siguientes cuatro tipos de paradigmas IO.
Bloqueo de IO : IO de subproceso único estándar en el que la aplicación espera a que se completen todas las operaciones de IO antes de continuar. Fácil de codificar, sin concurrencia y tan lento para aplicaciones que requieren múltiples operaciones de IO. El proceso o subproceso dormirá mientras espera que ocurra la interrupción de IO.
IO asíncrono : IO con subprocesos en el que la aplicación utiliza subprocesos de ejecución para realizar operaciones de bloqueo de IO al mismo tiempo. Requiere código seguro para subprocesos, pero generalmente es más fácil de leer y escribir que la alternativa. Obtiene la sobrecarga de varios hilos, pero tiene rutas de ejecución claras. Puede requerir el uso de métodos y contenedores sincronizados.
IO no bloqueante: IO de subproceso único en el que la aplicación utiliza select para determinar qué operaciones de IO están listas para avanzar, lo que permite la ejecución de otro código u otras operaciones de IO mientras el SO procesa IO simultáneo. El proceso no se suspende mientras se espera la interrupción de IO, pero asume la responsabilidad de verificar el indicador de IO en el identificador de archivo. Código mucho más complicado debido a la necesidad de verificar el indicador IO con select, aunque no requiere código thread-safe ni contenedores y métodos sincronizados. Baja ejecución por encima a expensas de la complejidad del código. Las rutas de ejecución son intrincadas.
IO asíncrono sin bloqueo : un enfoque híbrido para IO dirigido a reducir la complejidad mediante el uso de subprocesos, al tiempo que mantiene la escalabilidad mediante el uso de operaciones IO sin bloqueo cuando sea posible. Este sería el tipo más complejo de IO que requiere métodos y contenedores sincronizados, así como rutas de ejecución intrincadas. Este no es el tipo de IO que uno debería considerar codificar a la ligera, y con mucha frecuencia solo se usa cuando se usa una biblioteca que enmascara la complejidad, algo así como Futures and Promises.
Yo diría que hay tres tipos de io:
bloqueo sincrónico
sincrónico sin bloqueo
asincrónico
Tanto el sincrónico sincrónico como el sincrónico se considerarían no bloqueantes ya que el hilo de llamada no está esperando a que se complete el IO. Por lo tanto, aunque el io asincrónico sin bloqueo podría ser redundante, no son uno en el mismo. Cuando abro un archivo, puedo abrirlo en modo no bloqueante. ¿Qué significa esto? Significa que cuando publico una lectura () no se bloqueará. Me devolverá los bytes que están disponibles o indicará que no hay bytes disponibles. Si no habilitaba el no bloqueo io la lectura () se bloquearía hasta que los datos estuvieran disponibles. Es posible que desee habilitar io no bloqueante si quiero que un hilo maneje múltiples solicitudes io. Por ejemplo, podría usar select () para descubrir qué descriptores de archivos, o tal vez conectores, tienen datos disponibles para leer. Luego hago lecturas sincrónicas en esos descriptores de archivo. Ninguna de esas lecturas debe bloquearse porque ya sé que los datos están disponibles, además he abierto los descriptores de archivos en modo no bloqueante.
Asincrónico io es donde se emite una solicitud io. Esa solicitud está en cola y, por lo tanto, no bloquea el hilo emisor. Se le notifica cuando la solicitud falló o se completó exitosamente.
IO no bloqueante es cuando la llamada para realizar IO regresa inmediatamente, y no bloquea su hilo.
La única manera de saber si el IO está hecho, es sondear su estado o bloque. Piense en ello como un Future
. Inicia una operación IO y le devuelve un Future
. Puedes llamar a isDone()
para comprobar si está hecho, si es así, haz lo que quieras con él, de lo contrario sigue haciendo otras cosas hasta la próxima vez que quieras comprobar si está hecho. O bien, si no tiene nada que hacer, puede llamar a get
it, que se bloqueará hasta que esté listo.
Async IO es cuando la llamada para realizar IO le notifica que se realiza a través de un evento, no a través de su valor de retorno.
Esto puede ser de bloqueo o no bloqueo.
Bloqueo Async IO
Lo que se entiende por bloquear async IO es que la llamada para realizar IO es una llamada de bloqueo normal, pero lo que usted llamó envolvió esa llamada dentro de un hilo que se bloqueará hasta que se complete el IO y luego delegue el manejo del resultado del IO a su devolución de llamada. Es decir, todavía hay un hilo más abajo en la pila que está bloqueado en el IO, pero su hilo no lo está.
Async IO sin bloqueo
En realidad, este es el más común, y significa que el IO no bloqueante no necesita ser consultado para conocer su estado, como con IO estándar sin bloqueo, sino que llamará a su devolución de llamada cuando esté listo. A diferencia del bloqueo asincrónico IO, este no tiene hilos bloqueados en ningún lugar de la pila, por lo tanto, es más rápido y utiliza menos recursos, ya que el comportamiento asíncrono se gestiona sin bloquear hilos.
Puedes pensarlo como un CompletableFuture
. Requiere que su programa tenga alguna forma de marco de eventos asíncrono, que puede ser de subprocesos múltiples o no. Por lo tanto, es posible que la devolución de llamada se ejecute en otro hilo, o que esté programado para su ejecución en un hilo existente una vez que se haya completado la tarea actual.
Explico la distinción más detalladamente here.