Usando inputStream.available ()
Siempre es aceptable que System.in.available () devuelva 0.
He encontrado lo contrario: siempre devuelve el mejor valor para la cantidad de bytes disponibles. Javadoc para InputStream.available()
:
Returns an estimate of the number of bytes that can be read (or skipped over)
from this input stream without blocking by the next invocation of a method for
this input stream.
Una estimación es inevitable debido al tiempo / estancamiento. La cifra puede ser una subestimación única porque constantemente llegan nuevos datos. Sin embargo, siempre "se pone al día" en la próxima llamada: debe tener en cuenta todos los datos recibidos, salvo el que llega justo en el momento de la nueva llamada. Devolver permanentemente 0 cuando hay datos falla la condición anterior.
Primera advertencia: las subclases concretas de InputStream son responsables de las disponibles ()
InputStream
Es una clase abstracta. No tiene fuente de datos. No tiene sentido tener datos disponibles. Por lo tanto, javadoc para available()
también afirma:
The available method for class InputStream always returns 0.
This method should be overridden by subclasses.
Y, de hecho, las clases concretas de flujo de entrada anulan disponible (), proporcionando valores significativos, no ceros constantes.
Segunda advertencia: asegúrese de utilizar el retorno de carro al escribir la entrada en Windows.
Si lo usa System.in
, su programa solo recibe información cuando su shell de comando lo entrega. Si está utilizando redirección / canalización de archivos (por ejemplo, algún archivo> java myJavaApp o somecommand | java myJavaApp), los datos de entrada generalmente se entregan de inmediato. Sin embargo, si escribe manualmente la entrada, la transferencia de datos puede retrasarse. Por ejemplo, con el shell cmd.exe de Windows, los datos se almacenan en el shell cmd.exe. Los datos solo se pasan al programa Java en ejecución después del retorno de carro (control-mo <enter>
). Esa es una limitación del entorno de ejecución. Por supuesto, InputStream.available () devolverá 0 durante el tiempo que el shell guarde los datos, es el comportamiento correcto; No hay datos disponibles en ese momento. Tan pronto como los datos estén disponibles desde el shell, el método devuelve un valor> 0. NB: Cygwin usa cmd.
La solución más simple (sin bloqueo, por lo que no se requiere tiempo de espera)
Solo usa esto:
byte[] inputData = new byte[1024];
int result = is.read(inputData, 0, is.available());
// result will indicate number of bytes read; -1 for EOF with no data read.
O equivalente,
BufferedReader br = new BufferedReader(new InputStreamReader(System.in, Charset.forName("ISO-8859-1")),1024);
// ...
// inside some iteration / processing logic:
if (br.ready()) {
int readCount = br.read(inputData, bufferOffset, inputData.length-bufferOffset);
}
Solución más rica (llena al máximo el búfer dentro del tiempo de espera)
Declara esto:
public static int readInputStreamWithTimeout(InputStream is, byte[] b, int timeoutMillis)
throws IOException {
int bufferOffset = 0;
long maxTimeMillis = System.currentTimeMillis() + timeoutMillis;
while (System.currentTimeMillis() < maxTimeMillis && bufferOffset < b.length) {
int readLength = java.lang.Math.min(is.available(),b.length-bufferOffset);
// can alternatively use bufferedReader, guarded by isReady():
int readResult = is.read(b, bufferOffset, readLength);
if (readResult == -1) break;
bufferOffset += readResult;
}
return bufferOffset;
}
Entonces usa esto:
byte[] inputData = new byte[1024];
int readCount = readInputStreamWithTimeout(System.in, inputData, 6000); // 6 second timeout
// readCount will indicate number of bytes read; -1 for EOF with no data read.
is.available() > 1024
esta sugerencia fallará. Ciertamente, hay flujos que devuelven cero. SSLSockets, por ejemplo, hasta hace poco. No puedes confiar en esto.Suponiendo que su transmisión no esté respaldada por un socket (por lo que no puede usarla
Socket.setSoTimeout()
), creo que la forma estándar de resolver este tipo de problema es usar un Futuro.Supongamos que tengo el siguiente ejecutor y transmisiones:
Tengo un escritor que escribe algunos datos y luego espera 5 segundos antes de escribir el último dato y cerrar la transmisión:
La forma normal de leer esto es la siguiente. La lectura se bloqueará indefinidamente para los datos y esto se completa en 5s:
que salidas:
Si hubiera un problema más fundamental, como si el escritor no respondiera, el lector bloquearía para siempre. Si finalizo la lectura en un futuro, puedo controlar el tiempo de espera de la siguiente manera:
que salidas:
Puedo atrapar la TimeoutException y hacer la limpieza que quiera.
fuente
executer.shutdownNow
No matará el hilo. Intentará interrumpirlo, sin efecto. No hay limpieza posible y este es un problema grave.Si su InputStream está respaldado por un Socket, puede establecer un tiempo de espera de Socket (en milisegundos) usando setSoTimeout . Si la llamada read () no se desbloquea dentro del tiempo de espera especificado, arrojará una SocketTimeoutException.
Solo asegúrese de llamar a setSoTimeout en el Socket antes de hacer la llamada read ().
fuente
Yo cuestionaría el enunciado del problema en lugar de simplemente aceptarlo ciegamente. Solo necesita tiempos de espera de la consola o de la red. Si tiene este último
Socket.setSoTimeout()
yHttpURLConnection.setReadTimeout()
ambos hacen exactamente lo que se requiere, siempre que los configure correctamente cuando los construya / adquiera. Dejarlo en un punto arbitrario más adelante en la aplicación cuando todo lo que tiene es que InputStream es un diseño deficiente que conduce a una implementación muy incómoda.fuente
No he usado las clases del paquete Java NIO, pero parece que pueden ser de alguna ayuda aquí. Específicamente, java.nio.channels.Channels y java.nio.channels.InterruptibleChannel .
fuente
Aquí hay una manera de obtener un NIO FileChannel de System.in y verificar la disponibilidad de datos utilizando un tiempo de espera, que es un caso especial del problema descrito en la pregunta. Ejecútelo en la consola, no escriba ninguna entrada y espere los resultados. Fue probado con éxito bajo Java 6 en Windows y Linux.
Curiosamente, cuando se ejecuta el programa dentro de NetBeans 6.5 en lugar de en la consola, el tiempo de espera no funciona en absoluto, y la llamada a System.exit () es realmente necesaria para matar los hilos de zombies. Lo que sucede es que el hilo del interruptor bloquea (!) En la llamada a reader.interrupt (). Otro programa de prueba (que no se muestra aquí) también intenta cerrar el canal, pero tampoco funciona.
fuente
Como dijo jt, NIO es la mejor (y correcta) solución. Sin embargo, si realmente está atrapado con un InputStream, podría
Genera un hilo cuyo trabajo exclusivo es leer el InputStream y poner el resultado en un búfer que se puede leer desde su hilo original sin bloquear. Esto debería funcionar bien si solo tiene una instancia de la transmisión. De lo contrario, es posible que pueda eliminar el hilo utilizando los métodos obsoletos en la clase Thread, aunque esto puede causar pérdidas de recursos.
Confíe en isAvailable para indicar datos que se pueden leer sin bloquear. Sin embargo, en algunos casos (como con Sockets) puede tomar una lectura potencialmente bloqueante para isAvailable para informar algo distinto de 0.
fuente
Socket.setSoTimeout()
es una solución igualmente correcta y mucho más simple. OHttpURLConnection.setReadTimeout()
.Inspirado en esta respuesta, se me ocurrió una solución un poco más orientada a objetos.
Esto solo es válido si tiene la intención de leer caracteres
Puede anular BufferedReader e implementar algo como esto:
Aquí hay un ejemplo casi completo.
Estoy devolviendo 0 en algunos métodos, debería cambiarlo a -2 para satisfacer sus necesidades, pero creo que 0 es más adecuado con el contrato BufferedReader. No pasó nada malo, solo leía 0 caracteres. El método readLine es un asesino de rendimiento horrible. Debe crear un BufferedReader completamente nuevo si realmente desea usar readLin e. En este momento, no es seguro para subprocesos. Si alguien invoca una operación mientras readLines está esperando una línea, producirá resultados inesperados.
No me gusta regresar -2 donde estoy. Lanzaría una excepción porque algunas personas pueden simplemente verificar si int <0 para considerar EOS. De todos modos, esos métodos afirman que "no se puede bloquear", debe verificar si esa declaración es realmente cierta y simplemente no anularlos.
fuente