程式扎記: [ Java 常見問題 ] Is it possible to read from a InputStream with a timeout?

標籤

2015年1月19日 星期一

[ Java 常見問題 ] Is it possible to read from a InputStream with a timeout?

Source From Here 
Question 
Specifically, the problem is to write a method like this: 
  1. int maybeRead(InputStream in, long timeout)  
where the return value is the same as in.read() if data is available within 'timeout' milliseconds, and -2 otherwise. Before the method returns, any spawned threads must exit. 

How-To 
Assuming your stream is not backed by a socket (so you can't use Socket.setSoTimeout()), I think the standard way of solving this type of problem is to use aFuture. Suppose I have the following executor and streams: 
  1. ExecutorService executor = Executors.newFixedThreadPool(2);  
  2. final PipedOutputStream outputStream = new PipedOutputStream();  
  3. final PipedInputStream inputStream = new PipedInputStream(outputStream);  
I have writer that writes some data then waits for 5 seconds before writing the last piece of data and closing the stream: 
  1. Runnable writeTask = new Runnable() {  
  2.     @Override  
  3.     public void run() {  
  4.         try {  
  5.             outputStream.write(1);  
  6.             outputStream.write(2);  
  7.             Thread.sleep(5000);  
  8.             outputStream.write(3);  
  9.             outputStream.close();  
  10.         } catch (Exception e) {  
  11.             e.printStackTrace();  
  12.         }  
  13.     }  
  14. };  
  15. executor.submit(writeTask);  
The normal way of reading this is as follows. The read will block indefinitely for data and so this completes in 5s: 
  1. long start = System.currentTimeMillis();  
  2. int readByte = 1;  
  3. // Read data without timeout  
  4. while (readByte >= 0) {  
  5.     readByte = inputStream.read();  
  6.     if (readByte >= 0)  
  7.         System.out.println("Read: " + readByte);  
  8. }  
  9. System.out.println("Complete in " + (System.currentTimeMillis() - start) + "ms");  
  10. executor.shutdown()  
which outputs: 
Read: 1
Read: 2
Read: 3
Complete in 5001ms
If there was a more fundamental problem, like the writer not responding, the reader would block for ever. If I wrap the read in a future, I can then control the timeout as follows: 
  1. int readByte = 1;  
  2. // Read data with timeout  
  3. Callable readTask = new Callable() {  
  4.     @Override  
  5.     public Integer call() throws Exception   
  6.     {  
  7.         return inputStream.read();  
  8.     }  
  9. };  
  10. try  
  11. {  
  12.     while (readByte >= 0)   
  13.     {  
  14.         Future future = executor.submit(readTask);  
  15.         readByte = future.get(1000, TimeUnit.MILLISECONDS);  
  16.         if (readByte >= 0) System.out.println("Read: " + readByte);  
  17.     }  
  18. }  
  19. catch(Exception e){System.err.printf("\t[Error] %s\n", e)}  
which outputs: 
Read: 1
Read: 2

[Error] java.util.concurrent.TimeoutException

Supplement 
Java concurrency (multi-threading) - Tutorial

沒有留言:

張貼留言

網誌存檔