在什么条件下Java的Scanner.hasNextLine()会阻塞?

Scanner.hasNextLine()的javadoc指出:

如果此扫描器的输入中有另一行,则返回true。 此方法可能在等待输入时阻塞。 扫描仪不会超过任何输入。

该方法会在什么条件下阻止?

这取决于扫描仪从中获取输入的来源。

例如,如果它是一个文件,则整个输入都是可用的,因此hasNextLine()不会阻塞(因为它可以确定地知道何时到达文件的末尾并且没有更多的输入。

另一方面,如果源是标准输入,则总会有更多输入 – 用户总是可以输入更多输入 – 因此hasNextLine()会阻塞,直到用户键入新的输入行。

如何判断它是否会阻止?

遗憾的是,判断hasNextLine是否会阻塞是不受支持的用例。

这是因为底层源并不总是提供用于在流中窥视的API。 换句话说, hasNextLine的实现调用自身可能阻塞的方法,因此问题是固有的。


那么该怎么办?

如果这确实是一个必需的用例,我建议采用以下方法之一:

  • 确保条件适合hasNextLine 。 仅为扫描仪提供具有明确结束的源(例如文件或字符串), System.in诸如System.in类的“开放式”输入。

    如果这是API的一部分,您可以将扫描程序包装在您自己的类中,该类仅暴露“安全”构造函数。

  • 从头开始滚动你自己的类,它有一个willHasNextLineBlock类型的方法。 使用InputStream.available 可能会有点强大地实现这一点。


在超级丑陋的解决方法类别下,我们发现:

  • 尝试在单独的线程中调用hasNextLine ,看看它是否在合理的时间内返回,如下所示:

     boolean wouldBlock = false; Thread t = new Thread(() -> s.hasNextLine()); t.start(); try { t.join(100); } catch (InterruptedException e) { wouldBlock = true; } 
  • 使用自定义输入流(类似于可以在调用hasNextLine之前可以使用的可查看的流 。类似这样的东西

     CustomStream wrapped = new CustomStream(originalSource) Scanner s = new Scanner(wrapped); ... if (wrapped.hasNextLine()) // s.hasNextLine would not block else // s.hasNextLine would block 

    (但请注意,这有点不安全,因为扫描程序可能已从CustomStream缓冲了一些数据。)

假设“决定它是否会阻止”你的意思是你想知道什么时候会阻止

查看hasNextLine方法中输入的分配hasNextLine

 String result = findWithinHorizon(linePattern(), 0); 

现在,看一下findWithinHorizon方法

 public String findWithinHorizon(Pattern pattern, int horizon) { ensureOpen(); if (pattern == null) throw new NullPointerException(); if (horizon < 0) throw new IllegalArgumentException("horizon < 0"); clearCaches(); // Search for the pattern while (true) { //it may block here if it never break String token = findPatternInBuffer(pattern, horizon); if (token != null) { matchValid = true; return token; } if (needInput) readInput(); else break; // up to end of input } return null; } 

正如您所看到的,它将无限循环,直到达到结束,或者直到它成功读取。

findPatternInBufferScanner类的私有方法,它尝试读取输入。

 private String findPatternInBuffer(Pattern pattern, int horizon) { matchValid = false; matcher.usePattern(pattern); int bufferLimit = buf.limit(); int horizonLimit = -1; int searchLimit = bufferLimit; if (horizon > 0) { horizonLimit = position + horizon; if (horizonLimit < bufferLimit) searchLimit = horizonLimit; } matcher.region(position, searchLimit); if (matcher.find()) { if (matcher.hitEnd() && (!sourceClosed)) { // The match may be longer if didn't hit horizon or real end if (searchLimit != horizonLimit) { // Hit an artificial end; try to extend the match needInput = true; return null; } // The match could go away depending on what is next if ((searchLimit == horizonLimit) && matcher.requireEnd()) { // Rare case: we hit the end of input and it happens // that it is at the horizon and the end of input is // required for the match. needInput = true; return null; } } // Did not hit end, or hit real end, or hit horizon position = matcher.end(); return matcher.group(); } if (sourceClosed) return null; // If there is no specified horizon, or if we have not searched // to the specified horizon yet, get more input if ((horizon == 0) || (searchLimit != horizonLimit)) needInput = true; return null; } 

我发布了整个方法,让您更好地了解“成功阅读”的含义。