从InputStream读取-非阻塞与繁忙等待VS阻塞与超时?

时间:2019-02-11 16:36:39

标签: java inputstream blocking nonblocking

任务是从InputStream读取并等待结果长达可配置的时间。

给出两个选项,哪个更可取?或建议另一个。

  1. 阻止对必须超时的read()方法的调用
  2. 对available()的非阻塞调用,您必须使用忙于睡眠的轮询来轮询

    import java.io.IOException;
    import java.io.InputStream;
    import java.util.concurrent.*;
    
    public class MyClass {
    
        public static void main(String[] args) throws InterruptedException, ExecutionException, IOException {
            MyClass myClass = new MyClass();
            final InputStream in = System.in;
            final long timeout = 1000;
    
            final int result = myClass.blockingWithTimeout(in, timeout);
            //  final int result = myClass.nonBlockingBusyWait(in, timeout);
    
            System.out.println("Result " + result);
        }
    
        public int nonBlockingBusyWait(final InputStream is, long timeoutMs) throws IOException, InterruptedException {
            final long start = System.currentTimeMillis();
            while (is.available() == 0 && (System.currentTimeMillis() < start + timeoutMs)) {
                Thread.sleep(1);
            }
            if (is.available() == 0) {
                return -1;
            } else {
                return is.read();
            }
        }
    
        public int blockingWithTimeout(final InputStream is, long timeoutMs) throws InterruptedException, ExecutionException {
            ExecutorService es = Executors.newSingleThreadExecutor();
            Future<Integer> future = es.submit((Callable<Integer>) is::read);
            try {
                return future.get(timeoutMs, TimeUnit.MILLISECONDS);
    
            } catch (TimeoutException e) {
                return -1;
            } catch (InterruptedException | ExecutionException e) {
                throw e;
            } finally {
                future.cancel(true);
            }
        }
    
    }
    

1 个答案:

答案 0 :(得分:1)

两种方法都有缺陷。使用available()时,如果出现EOF,您的程序可能会永远挂起。提交阻塞的read()时,有可能后台调用将在超时后的 中完成并使用流中的数据。因此,数据将丢失。

当流是套接字流时,可以设置套接字超时并在代码中处理SocketTimeoutException。现代Java还为非套接字I / O类型提供了异步API。您可以调用其返回Future的方法。但是,如果您决定取消或放弃Future,则该频道可能会标记为不一致,并拒绝进一步操作。

如果是一些旧的第三方API返回了黑盒InputStream,则可以使用我的包装器获得类似套接字的行为。它将您不消耗的字节保存在内部缓冲区中:https://gist.github.com/basinilya/a5392de106cd890a28742960bcc5cf8c