Подтвердить что ты не робот

В каких условиях блокирует Java Scanner.hasNextLine()?

В javadoc для Scanner.hasNextLine() указано:

Возвращает true, если на входе этого сканера есть еще одна строка. Этот метод может блокироваться во время ожидания ввода. Сканер не продвигайтесь мимо каких-либо входных данных.

В каких условиях метод блокирует?

4b9b3361

Ответ 1

Это зависит от источника, из которого сканер получает вход.

Например, если это файл, доступен весь вход, поэтому hasNextLine() не будет блокироваться (поскольку он может знать с уверенностью, когда достигнут конец файла, и больше нет ввода.

С другой стороны, если источником является стандартный ввод, всегда может быть больше ввода - пользователь может всегда вводить больше ввода - поэтому hasNextLine() будет блокироваться до тех пор, пока пользователь не будет вводить новую строку ввода.

Ответ 2

Как решить, будет ли он блокироваться?

Чтобы решить, будет ли hasNextLine заблокирован или нет, к сожалению, это не поддерживается.

Это связано с тем, что базовые источники не всегда предоставляют API для поиска в потоке. Иными словами, реализация hasNextLine вызывает методы, которые сами могут блокировать, поэтому проблема является своего рода неотъемлемой.


Итак, что делать?

Если это действительно необходимый прецедент, я бы рекомендовал один из следующих способов:

  • Убедитесь, что условия соответствуют значению hasNextLine. Предоставляйте сканеру только те источники, которые имеют определенный конец (например, файл или строку) и никогда не имеют вход с открытым концом, например 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;
    }
    
  • Используйте настраиваемый поток ввода (что-то вроде 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.)

Ответ 3

Предполагая, что "решите, будет ли он блокироваться", вы имеете в виду, что хотите знать, когда он будет работать.

Посмотрите, где вход назначается в методе 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;
}

Как вы можете видеть, он будет бесконечно вращаться до тех пор, пока не будет достигнут конец, или пока не удастся его прочитать.

findPatternInBuffer - частный метод класса Scanner, который пытается прочитать ввод.

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

Я опубликовал весь метод, чтобы дать вам лучшее представление о том, что я имел в виду под "преуспеть для чтения".