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

时间:2015-03-24 14:21:26

标签: java

Scanner.hasNextLine()状态的javadoc:

  

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

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

3 个答案:

答案 0 :(得分:20)

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

例如,如果它是一个文件,那么整个输入都是可用的,因此hasNextLine()不会阻止(因为它可以确定地知道何时到达文件末尾并且#&# 39;不再输入。

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

答案 1 :(得分:4)

  

如何判断它是否会阻止?

为了确定hasNextLine是否会阻止,遗憾的是不支持用例。

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


那么,该怎么办?

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

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

    如果这是API的一部分,您可以将扫描仪包装在您自己的类中,只显示&#34; safe&#34;构造

  • 从头开始使用willHasNextLineBlock类型的方法滚动自己的类。这可能可能使用InputStream.available稍微强有力地实现。


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

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

    boolean wouldBlock = false;
    Thread t = new Thread(() -> s.hasNextLine());
    t.start();
    try {
        t.join(100);
    } catch (InterruptedException e) {
        wouldBlock = true;
    }
    
  • 使用自定义输入流(某些,如peekable stream,可以在调用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中的一些数据。)

答案 2 :(得分:2)

假设“决定它是否会阻止”你的意思是你想知道什么时候会出现这种情况。

查看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;
}

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