Java 是否可以在超时的情况下读取InputStream?
具体来说,问题在于编写一个如下方法:Java 是否可以在超时的情况下读取InputStream?,java,timeout,inputstream,Java,Timeout,Inputstream,具体来说,问题在于编写一个如下方法: int maybeRead(InputStream in, long timeout) public class SafeBufferedReader extends BufferedReader{ private long millisTimeout; ( . . . ) @Override public int read(char[] cbuf, int off, int len) throws IOExcepti
int maybeRead(InputStream in, long timeout)
public class SafeBufferedReader extends BufferedReader{
private long millisTimeout;
( . . . )
@Override
public int read(char[] cbuf, int off, int len) throws IOException {
try {
waitReady();
} catch(IllegalThreadStateException e) {
return 0;
}
return super.read(cbuf, off, len);
}
protected void waitReady() throws IllegalThreadStateException, IOException {
if(ready()) return;
long timeout = System.currentTimeMillis() + millisTimeout;
while(System.currentTimeMillis() < timeout) {
if(ready()) return;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
break; // Should restore flag
}
}
if(ready()) return; // Just in case.
throw new IllegalThreadStateException("Read timed out");
}
}
其中,如果数据在“超时”毫秒内可用,则返回值与.read()中的相同,否则返回值为-2。在方法返回之前,必须退出所有派生的线程
为了避免参数,这里的主题是java.io.InputStream,如Sun(任何java版本)所述。请注意,这并不像看上去那么简单。以下是Sun文档直接支持的一些事实
我还没有使用JavaNIO包中的类,但它们似乎在这里有一些帮助。具体地说,和。我没有使用Java NIO包中的类,但它们似乎在这里有一些帮助。具体来说,正如jt所说,NIO是最好(也是正确的)解决方案。如果你真的被输入流困住了,你可以
正如jt所说,NIO是最好(也是正确的)解决方案。如果你真的被输入流困住了,你可以
下面是一种从System.in获取NIO FileChannel并使用超时检查数据可用性的方法,这是问题中描述的问题的特例。在控制台上运行它,不要键入任何输入,然后等待结果。它在Windows和Linux上的Java6下测试成功
import java.io.FileInputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedByInterruptException;
public class Main {
static final ByteBuffer buf = ByteBuffer.allocate(4096);
public static void main(String[] args) {
long timeout = 1000 * 5;
try {
InputStream in = extract(System.in);
if (! (in instanceof FileInputStream))
throw new RuntimeException(
"Could not extract a FileInputStream from STDIN.");
try {
int ret = maybeAvailable((FileInputStream)in, timeout);
System.out.println(
Integer.toString(ret) + " bytes were read.");
} finally {
in.close();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/* unravels all layers of FilterInputStream wrappers to get to the
* core InputStream
*/
public static InputStream extract(InputStream in)
throws NoSuchFieldException, IllegalAccessException {
Field f = FilterInputStream.class.getDeclaredField("in");
f.setAccessible(true);
while( in instanceof FilterInputStream )
in = (InputStream)f.get((FilterInputStream)in);
return in;
}
/* Returns the number of bytes which could be read from the stream,
* timing out after the specified number of milliseconds.
* Returns 0 on timeout (because no bytes could be read)
* and -1 for end of stream.
*/
public static int maybeAvailable(final FileInputStream in, long timeout)
throws IOException, InterruptedException {
final int[] dataReady = {0};
final IOException[] maybeException = {null};
final Thread reader = new Thread() {
public void run() {
try {
dataReady[0] = in.getChannel().read(buf);
} catch (ClosedByInterruptException e) {
System.err.println("Reader interrupted.");
} catch (IOException e) {
maybeException[0] = e;
}
}
};
Thread interruptor = new Thread() {
public void run() {
reader.interrupt();
}
};
reader.start();
for(;;) {
reader.join(timeout);
if (!reader.isAlive())
break;
interruptor.start();
interruptor.join(1000);
reader.join(1000);
if (!reader.isAlive())
break;
System.err.println("We're hung");
System.exit(1);
}
if ( maybeException[0] != null )
throw maybeException[0];
return dataReady[0];
}
}
有趣的是,当在NetBeans 6.5内部而不是在控制台上运行程序时,超时根本不起作用,调用System.exit()实际上是杀死僵尸线程所必需的。发生的情况是中断程序线程在调用reader.interrupt()时阻塞(!)。另一个测试程序(此处未显示)另外尝试关闭通道,但这也不起作用。这里有一种方法可以从System.in获取NIO文件通道,并使用超时检查数据的可用性,这是问题中描述的问题的特例。在控制台上运行它,不要键入任何输入,然后等待结果。它在Windows和Linux上的Java6下测试成功
import java.io.FileInputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedByInterruptException;
public class Main {
static final ByteBuffer buf = ByteBuffer.allocate(4096);
public static void main(String[] args) {
long timeout = 1000 * 5;
try {
InputStream in = extract(System.in);
if (! (in instanceof FileInputStream))
throw new RuntimeException(
"Could not extract a FileInputStream from STDIN.");
try {
int ret = maybeAvailable((FileInputStream)in, timeout);
System.out.println(
Integer.toString(ret) + " bytes were read.");
} finally {
in.close();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/* unravels all layers of FilterInputStream wrappers to get to the
* core InputStream
*/
public static InputStream extract(InputStream in)
throws NoSuchFieldException, IllegalAccessException {
Field f = FilterInputStream.class.getDeclaredField("in");
f.setAccessible(true);
while( in instanceof FilterInputStream )
in = (InputStream)f.get((FilterInputStream)in);
return in;
}
/* Returns the number of bytes which could be read from the stream,
* timing out after the specified number of milliseconds.
* Returns 0 on timeout (because no bytes could be read)
* and -1 for end of stream.
*/
public static int maybeAvailable(final FileInputStream in, long timeout)
throws IOException, InterruptedException {
final int[] dataReady = {0};
final IOException[] maybeException = {null};
final Thread reader = new Thread() {
public void run() {
try {
dataReady[0] = in.getChannel().read(buf);
} catch (ClosedByInterruptException e) {
System.err.println("Reader interrupted.");
} catch (IOException e) {
maybeException[0] = e;
}
}
};
Thread interruptor = new Thread() {
public void run() {
reader.interrupt();
}
};
reader.start();
for(;;) {
reader.join(timeout);
if (!reader.isAlive())
break;
interruptor.start();
interruptor.join(1000);
reader.join(1000);
if (!reader.isAlive())
break;
System.err.println("We're hung");
System.exit(1);
}
if ( maybeException[0] != null )
throw maybeException[0];
return dataReady[0];
}
}
有趣的是,当在NetBeans 6.5内部而不是在控制台上运行程序时,超时根本不起作用,调用System.exit()实际上是杀死僵尸线程所必需的。发生的情况是中断程序线程在调用reader.interrupt()时阻塞(!)。另一个测试程序(此处未显示)另外尝试关闭通道,但也不起作用。如果您的InputStream由套接字支持,则可以使用设置套接字超时(以毫秒为单位)。如果read()调用没有在指定的超时内解除阻塞,它将抛出SocketTimeoutException
在进行read()调用之前,请确保在套接字上调用setSortimeout。如果您的InputStream由套接字支持,则可以使用设置套接字超时(以毫秒为单位)。如果read()调用没有在指定的超时内解除阻塞,它将抛出SocketTimeoutException
在进行read()调用之前,请确保在套接字上调用setSoTimeout。我会质疑问题陈述,而不是盲目地接受它。您只需要从控制台或通过网络超时。如果是后者,则有
Socket.setSoTimeout()
和HttpURLConnection.setReadTimeout()
,只要在构造/获取它们时正确设置它们,它们都能完成所需的任务。当你只有InputStream时,把它留在应用程序中的任意点上,因为它的设计很差,导致了一个非常笨拙的实现。我会质疑问题陈述,而不是盲目地接受它。您只需要从控制台或通过网络超时。如果是后者,则有Socket.setSoTimeout()
和HttpURLConnection.setReadTimeout()
,只要在构造/获取它们时正确设置它们,它们都能完成所需的任务。当您只有InputStream时,将其保留到应用程序中的任意点是糟糕的设计,导致了非常笨拙的实现。假设您的流没有套接字支持(因此您不能使用socket.setSortimeo
long start = currentTimeMillis();
int readByte = 1;
// Read data without timeout
while (readByte >= 0) {
readByte = inputStream.read();
if (readByte >= 0)
System.out.println("Read: " + readByte);
}
System.out.println("Complete in " + (currentTimeMillis() - start) + "ms");
Read: 1
Read: 2
Read: 3
Complete in 5001ms
int readByte = 1;
// Read data with timeout
Callable<Integer> readTask = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
return inputStream.read();
}
};
while (readByte >= 0) {
Future<Integer> future = executor.submit(readTask);
readByte = future.get(1000, TimeUnit.MILLISECONDS);
if (readByte >= 0)
System.out.println("Read: " + readByte);
}
Read: 1
Read: 2
Exception in thread "main" java.util.concurrent.TimeoutException
at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:228)
at java.util.concurrent.FutureTask.get(FutureTask.java:91)
at test.InputStreamWithTimeoutTest.main(InputStreamWithTimeoutTest.java:74)
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.
The available method for class InputStream always returns 0.
This method should be overridden by subclasses.
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.
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);
}
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;
}
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.
public class SafeBufferedReader extends BufferedReader{
private long millisTimeout;
( . . . )
@Override
public int read(char[] cbuf, int off, int len) throws IOException {
try {
waitReady();
} catch(IllegalThreadStateException e) {
return 0;
}
return super.read(cbuf, off, len);
}
protected void waitReady() throws IllegalThreadStateException, IOException {
if(ready()) return;
long timeout = System.currentTimeMillis() + millisTimeout;
while(System.currentTimeMillis() < timeout) {
if(ready()) return;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
break; // Should restore flag
}
}
if(ready()) return; // Just in case.
throw new IllegalThreadStateException("Read timed out");
}
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.nio.CharBuffer;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
/**
*
* readLine
*
* @author Dario
*
*/
public class SafeBufferedReader extends BufferedReader{
private long millisTimeout;
private long millisInterval = 100;
private int lookAheadLine;
public SafeBufferedReader(Reader in, int sz, long millisTimeout) {
super(in, sz);
this.millisTimeout = millisTimeout;
}
public SafeBufferedReader(Reader in, long millisTimeout) {
super(in);
this.millisTimeout = millisTimeout;
}
/**
* This is probably going to kill readLine performance. You should study BufferedReader and completly override the method.
*
* It should mark the position, then perform its normal operation in a nonblocking way, and if it reaches the timeout then reset position and throw IllegalThreadStateException
*
*/
@Override
public String readLine() throws IOException {
try {
waitReadyLine();
} catch(IllegalThreadStateException e) {
//return null; //Null usually means EOS here, so we can't.
throw e;
}
return super.readLine();
}
@Override
public int read() throws IOException {
try {
waitReady();
} catch(IllegalThreadStateException e) {
return -2; // I'd throw a runtime here, as some people may just be checking if int < 0 to consider EOS
}
return super.read();
}
@Override
public int read(char[] cbuf) throws IOException {
try {
waitReady();
} catch(IllegalThreadStateException e) {
return -2; // I'd throw a runtime here, as some people may just be checking if int < 0 to consider EOS
}
return super.read(cbuf);
}
@Override
public int read(char[] cbuf, int off, int len) throws IOException {
try {
waitReady();
} catch(IllegalThreadStateException e) {
return 0;
}
return super.read(cbuf, off, len);
}
@Override
public int read(CharBuffer target) throws IOException {
try {
waitReady();
} catch(IllegalThreadStateException e) {
return 0;
}
return super.read(target);
}
@Override
public void mark(int readAheadLimit) throws IOException {
super.mark(readAheadLimit);
}
@Override
public Stream<String> lines() {
return super.lines();
}
@Override
public void reset() throws IOException {
super.reset();
}
@Override
public long skip(long n) throws IOException {
return super.skip(n);
}
public long getMillisTimeout() {
return millisTimeout;
}
public void setMillisTimeout(long millisTimeout) {
this.millisTimeout = millisTimeout;
}
public void setTimeout(long timeout, TimeUnit unit) {
this.millisTimeout = TimeUnit.MILLISECONDS.convert(timeout, unit);
}
public long getMillisInterval() {
return millisInterval;
}
public void setMillisInterval(long millisInterval) {
this.millisInterval = millisInterval;
}
public void setInterval(long time, TimeUnit unit) {
this.millisInterval = TimeUnit.MILLISECONDS.convert(time, unit);
}
/**
* This is actually forcing us to read the buffer twice in order to determine a line is actually ready.
*
* @throws IllegalThreadStateException
* @throws IOException
*/
protected void waitReadyLine() throws IllegalThreadStateException, IOException {
long timeout = System.currentTimeMillis() + millisTimeout;
waitReady();
super.mark(lookAheadLine);
try {
while(System.currentTimeMillis() < timeout) {
while(ready()) {
int charInt = super.read();
if(charInt==-1) return; // EOS reached
char character = (char) charInt;
if(character == '\n' || character == '\r' ) return;
}
try {
Thread.sleep(millisInterval);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // Restore flag
break;
}
}
} finally {
super.reset();
}
throw new IllegalThreadStateException("readLine timed out");
}
protected void waitReady() throws IllegalThreadStateException, IOException {
if(ready()) return;
long timeout = System.currentTimeMillis() + millisTimeout;
while(System.currentTimeMillis() < timeout) {
if(ready()) return;
try {
Thread.sleep(millisInterval);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // Restore flag
break;
}
}
if(ready()) return; // Just in case.
throw new IllegalThreadStateException("read timed out");
}
}