Как определить точное состояние BufferedReader?

У меня есть a BufferedReader (сгенерированный new BufferedReader(new InputStreamReader(process.getInputStream()))). Я довольно плохо знаком с понятием a BufferedReader но поскольку я вижу его, это имеет три состояния:

  1. Строка ожидает, чтобы быть считанной; вызов bufferedReader.readLine возвратит эту строку немедленно.
  2. Поток открыт, но нет никакой строки, ожидающей, чтобы быть считанной; вызов bufferedReader.readLine подвесит поток, пока строка не станет доступной.
  3. Поток закрывается; вызов bufferedReader.readLine возвратит пустой указатель.

Теперь я хочу определить состояние BufferedReader, так, чтобы я мог определить, могу ли я безопасно читать из него, не подвешивая мое приложение. Базовый процесс (см. выше) известно ненадежен и так, возможно, завис; в этом случае я не хочу, чтобы мое хост-приложение зависло. Поэтому я реализую своего рода тайм-аут. Я пытался сделать это сначала с поточной обработкой, но она была ужасно сложной.

Вызов BufferedReader.ready() не будет различать случаи (2) и (3) выше. Другими словами, если ready() возвращает false, могло бы случиться так, что поток просто закрылся (другими словами, мой базовый процесс, закрытый корректно), или могло бы случиться так что базовый подвешенный процесс.

Таким образом, мой вопрос: как я определяю который из этих трех состояний мой BufferedReader находится в без фактического вызова readLine? К сожалению, я не могу просто звонить readLine проверять это, поскольку это открывает мое приложение до подвешивания.

Я использую версию 1.5 JDK.

7
задан Kidburla 23 January 2010 в 20:20
поделиться

8 ответов

Наконец я нашел решение этого. Большинство ответов здесь полагаются на темы, но, как я указываю ранее, я ищу решение, которое не требует потоков. Однако моя основа была процессом. То, что я нашел, было то, что процессы, кажется, выходят, если оба выхода (называемого «вход»), так и потоки ошибок пусты и закрыты. Это имеет смысл, если вы думаете об этом.

Итак, я только что опросил выходные и ошибки потоки, а также попытался определить, вышел ли процесс или нет. Ниже представляет собой грубую копию моего решения.

public String readLineWithTimeout(Process process, long timeout) throws IOException, TimeoutException {
  BufferedReader output = new BufferedReader(new InputStreamReader(process.getInputStream()));
  BufferedReader error = new BufferedReader(new InputStreamReader(process.getErrorStream()));
  boolean finished = false;
  long startTime = 0;
  while (!finished) {
    if (output.ready()) {
      return output.readLine();
    } else if (error.ready()) {
      error.readLine();
    } else {
      try {
        process.exitValue();
        return null;
      } catch (IllegalThreadStateException ex) {
        //Expected behaviour
      }
    }
    if (startTime == 0) {
      startTime = System.currentTimeMills();
    } else if (System.currentTimeMillis() > startTime + timeout) {
      throw new TimeoutException();
    }
  }
}
2
ответ дан 7 December 2019 в 12:20
поделиться

Есть состояние, в котором некоторые данные могут находиться в буфере, но не обязательно достаточно, чтобы заполнить строку. В этом случае ready() вернет true, но вызов readLine() заблокирует.

Вы легко сможете собрать свои собственные методы ready() и readLine(). Ваш ready() на самом деле будет пытаться построить строку, и только после этого он вернет true. Тогда ваша readLine() может вернуть полностью сформированную строку.

2
ответ дан 7 December 2019 в 12:20
поделиться

Это довольно фундаментальная проблема с блокировкой Java I / O API.

Я подозреваю, что вы хотите выбрать один из:

(1) повторное посещение идеи использования резьбы. Это не должно быть сложно, выполнено правильно, и он позволил бы вашему коду избежать блокированного ввода / вывода, читающего довольно изящно, например:

final BufferedReader reader = ...
ExecutorService executor = // create an executor here, using the Executors factory class.
Callable<String> task = new Callable<String> {
   public String call() throws IOException {
      return reader.readLine();
   }
};
Future<String> futureResult = executor.submit(task);
String line = futureResult.get(timeout);  // throws a TimeoutException if the read doesn't return in time

(2) Использование Java.nio вместо Java.io . Это более сложный API, но он имеет неблокирую семантику.

1
ответ дан 7 December 2019 в 12:20
поделиться

Утвердили ли вы экспериментируете ваше утверждение, которое готово () вернет ложь, даже если основной поток находится в конце файла? Потому что я не ожидал, что утверждение будет правильным (хотя я не сделал эксперимент).

0
ответ дан 7 December 2019 в 12:20
поделиться

Вы можете использовать INPUTSTREAM. Доступный (), чтобы увидеть, появится ли новый вывод из процесса. Это должно работать так, как вы хотите, если процесс выводит только полные линии, но это не очень надежно.

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

0
ответ дан 7 December 2019 в 12:20
поделиться

В общем вы должны реализовать это с несколькими потоками. Существуют особые случаи, такие как чтение от сокета, где базовый поток имеет встроенный встроен в TimeOut.

Тем не менее, это не должно быть ужасно сложно сделать это с несколькими потоками. Это узор, который я использую:

private static final ExecutorService worker = 
  Executors.newSingleThreadExecutor();

private static class Timeout implements Callable<Void> {
  private final Closeable target;
  private Timeout(Closeable target) {
    this.target = target;
  }
  public Void call() throws Exception {
    target.close();
    return null;
  }
}

...

InputStream stream = process.getInputStream();
Future<?> task = worker.schedule(new Timeout(stream), 5, TimeUnit.SECONDS);
/* Use the stream as you wish. If it hangs for more than 5 seconds, 
   the underlying stream is closed, raising an IOException here. */
...
/* If you get here without timing out, cancel the asynchronous timeout 
  and close the stream explicitly. */
if(task.cancel(false))
  stream.close();
0
ответ дан 7 December 2019 в 12:20
поделиться

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

public class MyReader implements Runnable {
    private final BufferedReader reader;
    private ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<String>();
    private boolean closed = false;

    public MyReader(BufferedReader reader) {
        this.reader = reader;
    }

    public void run() {
        String line;
        while((line = reader.readLine()) != null) {
            queue.add(line);
        }
        closed = true;
    }

    // Returns true iff there is at least one line on the queue
    public boolean ready() {
        return(queue.peek() != null);
    }

    // Returns true if the underlying connection has closed
    // Note that there may still be data on the queue!
    public boolean isClosed() {
        return closed;
    }

    // Get next line
    // Returns null if there is none
    // Never blocks
    public String readLine() {
        return(queue.poll());
    }
}

Вот как его использовать:

BufferedReader b; // Initialise however you normally do
MyReader reader = new MyReader(b);
new Thread(reader).start();

// True if there is data to be read regardless of connection state
reader.ready();

// True if the connection is closed
reader.closed();

// Gets the next line, never blocks
// Returns null if there is no data
// This doesn't necessarily mean the connection is closed, it might be waiting!
String line = reader.readLine(); // Gets the next line

Есть четыре возможных состояния:

  1. соединение открыто, никакие данные не доступны
  2. соединение открыто, данные доступны
  3. соединение закрыто, доступно данные
  4. соединение закрыто, никаких данных Доступно

Вы можете различить их с помощью раскрытых () и готовых () методов.

0
ответ дан 7 December 2019 в 12:20
поделиться

Вы можете сделать свою собственную обертку в Inputstream или Inputstreamreader, который работает на байтовом уровне, для которого готовы () возвращает точные значения.

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

0
ответ дан 7 December 2019 в 12:20
поделиться
Другие вопросы по тегам:

Похожие вопросы: