java.nio y java.io

Veo que no hay prácticamente nada información acerca de este tema en el sitio, así que decidí escribir esto esperando que a alguien le resulte útil.

Desde la versión 1.4 de Java apareció un paquete nuevo, java.nio, similar a java.io; esa n es de non-blocking. Los que hayan usado InputStream y OutputStream para leer por ejemplo de un socket, sabrán que los métodos de lectura y escritura bloquean el thread que los invoca, hasta que terminen su operación. Por lo tanto, en ambientes donde se tienen un alto volumen de intercambio de datos por medio de sockets, normalmente se tiene maneja una cola de mensajes que se deben enviar, junto con un thread dedicado a tomar mensajes de dicha cola y escribirlos al socket; y por otra parte se tiene un thread dedicado a leer del socket continuamente, poniendo en una cola los mensajes que van llegando (ojo: aquí cuando digo "mensajes" me refiero a tramas de datos que llegan por el socket que vienen delimitadas de alguna forma, pero aun sin parsear).

Pues bien, el problema es peor cuando se programa algun tipo de servidor, es decir, se tiene un ServerSocket aceptando conexiones entrantes y luego se tiene que hacer lo que describí en el párrafo anterior, para cada conexión. De hecho el ciclo de estar aceptando conexiones nuevas debe ir en un thread dedicado. Así que tenemos ese thread, y por cada conexión que llega, tendremos probablemente dos threads. Cuando ya manejamos 50 conexiones, estamos hablando de 101 threads. Es un problema de escalabilidad porque cada thread utiliza recursos: memoria, CPU, etc.

Este es precisamente el problema que java.nio resuelve. En vez de leer y escribir directamente a los streams de un socket, se manejan buffers que se le pasan a los sockets (llamados SocketChannels) y que el socket escribirá en su tiempo. También hay un mecanismo que no es precisamente de notificación pero que permite saber cuando un socket tiene datos en su buffer para que los leamos. Esto, en el ejemplo del servidor, nos permite implementar un solo ciclo donde vamos aceptando conexiones y además vamos leyendo datos de los sockets que ya tenemos abiertos.

Esto va a quedar más claro con un ejemplo. Primero vamos con el tradicional enfoque de java.io (en nombre de la brevedad, me voy a saltar algunos try/catch de IOException y ciertas declaraciones no tan relevantes):

 

Entonces tenemos que el Servidor corre en un thread, y solamente es un ciclo que va aceptando conexiones. El metodo accept() bloquea el thread hasta que alguien se conecta al puerto donde está escuchando; entonces crea una conexión y la corre en un thread separado. La conexión en su thread crea un objeto Escritor, que corre en otro thread, y luego entra en un ciclo donde lee del socket (el método socket.read() bloquea el thread hasta obtener datos, si no se definió un timeout de lectura en dicho socket). Cuando tiene listo un mensaje completo, simplemente lo pasa al Escritor, donde se encola para que su propio thread lo tome en cuanto haya un mensaje disponible y se escribe al socket.
Y aquí ni siquiera tenemos el paisaje completo; el procesamiento de los datos de entrada debería hacerse en otro thread, precisamente para no interrumpir la lectura de datos y que no vayamos a perder nada; dicho thread (que ya no incluí para no complicar más las cosas) posteriormente es quien debe pasar el mensaje al Escritor.

Bastante complicado, no? sobre todo para un sencillo ejemplo de servidorsito en Java... Ahora vamos a ver la versión usando java.nio:

 

En este caso el código de la clase Servidor creció bastante, pero veamos el manejo de threads: no no podemos salvar de tener un thread dedicado, en este caso para el ciclo principal del Servidor. Sin embargo, el Servidor está haciendo más cosas ahora, todo dentro de ese mismo thread; cuando recibe una conexión nueva, crea un objeto para manejar los datos que van a llegar por dicha conexión (clase Conexion). Pero también, cuando llegan datos por una de las conexiones existentes, el Servidor va a pedir al objeto que la administra, que lea datos de ahí, y si ya se leyó un mensaje completo, se procesa (eso lo hace otro objeto, Procesador) y se escribe la respuesta.
De manera que tenemos un solo thread encargado de aceptar conexiones nuevas, leer datos de las conexiones existentes, e incluso procesar los datos y enviar las respuestas. Esto último no es lo óptimo; sin embargo es muy fácil incluir un thread pool en el servidor para realizar ese paso. Solamente hay que crear algo así en la clase Servidor:

 

y luego dentro del if de lectura de datos completa:

 

Con eso ya el procesamiento se va a hacer dentro del thread pool.

Así que como pueden ver, el uso de java.nio aunque es un paradigma algo distinto de estar leyendo directo de los streams, simplifica el funcionamiento de las aplicaciones y no requiere tener threads dedicados para lectura/escritura de datos.

IMPORTANTE: Esta noticia salió en julio de 2010, donde se hicieron unos benchmarks y resulta que ahora IO es nuevamente más rápido que NIO, al menos en Linux, debido a los cambios en la implementación de threads a nivel sistema operativo; ahora usar java.io con threads dedicados resulta ser de 25% a 35% más rápido que NIO, porque usar threads separados ya es más rápido que los cambios de contexto dentro del mismo thread (que es lo que se hace en el ciclo de java.nio que describo en este post).

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.
Imagen de 1a1iux

FileChannel

En el mismo paquete java.nio se incluye la clase FileChannel.

Una de las principales ventajas de hacer uso de un FileChannel es que las operaciones de lectura y escritura se realizarán muy rápido si el sistema operativo las puede optimizar. Se dice que se pueden mover datos directamente de y hacía la caché del sistema de archivos con el uso de estos canales.

A continuación un ejemplo de como se haría la copia de un archivo desde java.

 

 

Sale y vale
Byte

Imagen de Nopalin

No pude

Hace tiempo realize un sencillo juego de mesa multiplayer utilizando java.io, el programa quedo funcionando pero todavia hace falta pulir muchas cosas, el proyecto para el que le interese esta en mi blog en este sitio, sin embargo como ya habia leido sobre esto quize implementarlo usando java.nio y simplemente no operó como esperaba o no supe como implementarlo que es lo más seguro jeje, si alguien puede tomar la idea e implementarlo en nio, pus estaria chido.

sobres