Что происходит с BufferedReader, который не закрывается внутри вызываемого объекта.позвонить?


У меня есть три вопроса.

Чтобы объяснить, я просматривал чей-то код и заметил, что BufferedReaderиногда не закрываются. Обычно Eclipse предупреждает, что это потенциальная утечка памяти (и я ее исправляю). Однако внутри вызываемого внутреннего класса нет никакого предупреждения.
class outerClass {
    ...
    public void someMethod() {
        Future<Integer> future = outputThreadPool.submit(new innerClass(this.myProcess.getInputStream(), threadName));
        ...
    }

    class innerClass implements Callable<Integer> {
        private final InputStream stream;
        private final String prepend;

        innerClass(InputStream stream, String prepend) {
            this.stream = stream;
            this.prepend = prepend;
        }

        @Override
        public Integer call() {
            BufferedReader stdOut = new BufferedReader(new InputStreamReader(stream));
            String output = null;
            try {
                while ((output = stdOut.readLine()) != null) {
                    log.info("[" + prepend + "] " + output);
                }

            } catch (IOException ignore) {
            // I have no idea why we're ignoring this... :-|        
            }
            return 0;   
        }
    }
}
Люди, написавшие этот код, являются опытными разработчиками Java, поэтому моя первая мысль заключается в том, что это преднамеренно... но может быть, они торопились, когда писали его, и просто проглядели оно.

Мои вопросы:

  1. Почему Eclipse не выделяет это (на что можно ответить ответом на следующие вопросы)?

  2. Что самое худшее, что может произойти, если он закрыт в методе call ()? (Я не могу придумать хорошей причины... и я уже некоторое время занимаюсь поисками... но, возможно, это было намеренно, чтобы не закрыть BufferedReader)

  3. Что самое худшее, что может произойти, если BufferedReader Нет закрыто внутри внутреннего класса?

5 8

5 ответов:

Я бы сказал, что, поскольку они создают BufferedReader вокруг заданного InputStream, код безопасен, не вызывая close(). Код, вызывающий close(), всегда должен быть кодом, создающим поток и выполняемым с помощью try / finally.

public static void read(String str) throws IOException {
    FileInputStream stream = null
    try {
        stream = new FileInputStream(str);
        readStreamToConsole(stream);
    } finally {
        if (stream != null)
            stream.close();
    }
}

private static void readStreamToConsole(InputStream stream) {
    BufferedReader stdOut = new BufferedReader(new InputStreamReader(stream));
    String output = null;
    while ((output = stdOut.readLine()) != null)
        System.out.println(output);
}

Еще одно замечание: ваш код, похоже, регистрирует выходные данные из какого-то другого процесса. Вы, вероятно, не сможете закрыть поток в любом случае. Не проверяя его Сам, я не уверен, что произойдет, если вы закроете поток из другого процесса.

О, и IOException вряд ли произойдет, потому что поток исходит из другого процесса. Это вряд ли произойдет, если не произойдет какая-то неисправимая ошибка. Тем не менее, было бы неплохо как-то зарегистрировать исключение.


Отредактируйте свой комментарий о смешанных ответах:

Будем использовать выходной поток и BufferedWriter в качестве примера на этот раз:

private static final String NEWLINE = System.getProperty("line.separator");

public static void main(String[] args) throws IOException {
    String file = "foo/bar.txt";
    FileOutputStream stream = null;
    try {
        stream = new FileOutputStream(file);
        writeLine(stream, "Line 1");
        writeLine(stream, "Line 2");
    } finally {
        if (stream != null)
            stream.close();
    }
}

private static void writeLine(OutputStream stream, String line) throws IOException {
    BufferedWriter writer = new BufferedWriter(new InputStreamWriter(stream));
    writer.write(line + NEWLINE);
}

Это работает. Метод writeLine используется в качестве делегата для создания writer и фактически запись одного line в файл. Конечно, эта логика может быть чем-то более сложным, например, превратить объект в String и записать его. Это также облегчает чтение метода main.

А что, если вместо этого мы закроем BufferedWriter?

private static void writeLine(OutputStream stream, String line) throws IOException {
    BufferedWriter writer = null;
    try {
        writer = new BufferedWriter(new InputStreamWriter(stream));
        writer.write(line + NEWLINE);
    } finally {
        if (writer != null)
            writer.close();
    }
}
Попробуйте запустить его с этим, и он будет терпеть неудачу каждый раз при втором вызове writeLine. Хорошая практика-всегда закрывать потоки там, где они создаются, а не там, где они передаются. Это может быть нормально изначально, но попытка изменить этот код позже может привести к ошибкам. Если бы я начал только с 1 writeLine вызова с плохим методом, а кто-то другой захотел бы добавить второй, им пришлось бы рефакторировать код, чтобы writeLine не закрывал поток в любом случае. Близость к счастью может вызвать головную боль в будущем.

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

Итак, правило thumb: закрывайте потоки только там, где вы их создаете, и всегда делайте создание и закрытие в блоке try/finally (или awesome try/resource block Java 7, который делает закрытие за вас).

BuffereddReader закрыть()

Закрывает этот поток и освобождает все системные ресурсы, связанные с оно. Если поток уже закрыт, то вызов этого метода не имеет никакого значения. эффект.

Таким образом, если вы не закроете (), системные ресурсы все еще могут быть связаны с устройством чтения, что может привести к утечке памяти.

Почему eclipse не выделяет: это не ошибка времени компиляции, если вы игнорируете вызов close (), поэтому eclipse не выделяет.

В этом случае вы можете не закрывать BufferedReader. InputStream, который был передан конструктору, является объектом, который может быть связан с системным ресурсом. BufferedReader и InputStreamReader - это просто обертки вокруг этого. Закрытие BufferedReader также закроет InputStream, что может быть не тем, что хотел вызывающий.

  1. Не выделяет, и это правда, потому что поток может быть закрыт где-то еще из метода вызова.

  2. Если он закрыт внутри метода вызова, то другие потоки могут использовать его в данный момент.

  3. С BufferdRreader ничего, но если вы потеряете ссылку на поток и не можете закрыть его, и это приводит к утечке памяти.

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

   BufferedReader stdOut = null;
   String output = null;
        try {
            stdOut = new BufferedReader(new InputStreamReader(stream));
            while ((output = stdOut.readLine()) != null) {
                log.info("[" + prepend + "] " + output);
            }
        } catch (FileNotFoundException ex) {
            log.warn("Unable to open nonexisten file " + whichOne);  
        } catch (IOException ex) {
            log.warn("Unable to read from stream");  
        } finally {
            if (stdOut != null) {
                try {
                   stdOut.close();
                } catch (IOException e) {
                    log.warn("Unable to close the stream");
                }
            }
        }
        return 0;

Или, если Вам разрешено использовать Java 7, Вы можете использовать преимущества интерфейса AutoCloseable и новой структуры языка для этой цели. видеть http://www.oracle.com/technetwork/articles/java/trywithresources-401775.html