Java попробуй наконец вариации - PullRequest
5 голосов
/ 28 апреля 2010

Этот вопрос некоторое время не давал мне покоя, но я не нашел полного ответа на него (например, этот вопрос для C # Инициализация доступных ресурсов снаружи или внутри try / finally ). Рассмотрим два следующих фрагмента кода Java:

Closeable in = new FileInputStream("data.txt");
try {
    doSomething(in);
} finally {
    in.close();
}

и второй вариант

Closeable in = null;
try {
    in = new FileInputStream("data.txt");
    doSomething(in);
} finally {
    if (null != in) in.close();
}

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

  1. InterruptedException (например, через Thread # interrupt ()) или генерируется исключение OutOfMemoryError
  2. JVM завершает работу (например, с помощью kill, System.exit ())
  3. Аппаратный сбой (или ошибка в JVM для полного списка:)

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

Итак, вопрос:

В чем различия между двумя? Что мне следует предпочесть, если я беспокоюсь об освобождении ресурсов (особенно в многопоточных приложениях)? Почему?

Буду признателен, если кто-нибудь укажет мне на части спецификаций Java / JVM, которые поддерживают ответы.

Ответы [ 3 ]

6 голосов
/ 28 апреля 2010

Не думаю, что есть основания для беспокойства:

1) InterruptedException (например, через Thread # interrupt ())

Вызов Thread.interrupt() не вызывает самопроизвольного выброса InterruptedException. Исключение возникает только в определенных (и хорошо документированных) методах блокировки; т.е. блокировка ввода / вывода и методы синхронизации. Это исключение не может быть сгенерировано после возврата из потокового конструктора и перед входом в блок try.

или исключение OutOfMemoryError выбрасывается

Если выдается OutOfMemoryError, вы не можете гарантировать полное восстановление базового файлового дескриптора, независимо от того, куда вы поместили конструктор потока. Вы никогда не должны пытаться восстанавливаться из OOM, поэтому вопрос о том, закрыт ли поток, является спорным. Кроме того, это исключение создается только в потоке, который на самом деле пытается выделить память, и на данный момент этого не происходит.

2) JVM завершает работу (например, через kill, System.exit ())

Если приложение принудительно завершается внешним вызовом kill или System.exit(), не имеет значения, правильно ли закрыты потоки. Кроме того, в обоих случаях нет гарантии, что пункты finally будут выполнены.

3) Аппаратный сбой (или ошибка в JVM для полного списка:)

Все ставки сняты. У вас нет возможности узнать, выполнится ли что-нибудь, не говоря уже о блоках finally.

Существует еще одна ситуация, когда поток может получить спонтанное исключение в этот момент с некоторым (наивным) ожиданием, что он может восстановиться. Именно тогда какой-то заблуждающийся программист решает вызвать устаревший метод Thread.stop(). Вы можете подумать, что размещение вызова конструктора потока внутри блока try поможет. Но на самом деле это не так, потому что исключение ThreadDeath может быть вызвано внутри конструктора потока между открытием базового файла и завершением создания объекта потока. Так что FD может протечь в любом случае.

Это одна из причин, по которой Thread.stop() устарела. Не используйте его.

5 голосов
/ 28 апреля 2010

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

Closeable in = new FileInputStream("data.txt");

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

б) относительно OutOfMemoryError - я не вижу, как это может произойти сразу после построения входного потока. Это может произойти в другом потоке, но это не окажет немедленного влияния на этот поток. Проблема с OutOfMemoryError заключается в том, что ваш блок finally также может завершиться сбоем, поскольку для его завершения недостаточно памяти ...

в) Единственный способ узнать, что поток может быть прерван агрессивно, - использовать устаревшие методы Thread.stop () и Thread.stop (Throwable). Смотрите подобное обсуждение здесь: Это безопасный способ высвобождения ресурсов в Java?

0 голосов
/ 28 апреля 2010

Я считаю, что когда вы работаете с управляемой средой выполнения, такой как Java или .NET, вам не следует (и это хорошо!) Заниматься такими вещами, как ваш конкретный вопрос. Только потому, что вы полностью отключены от базовой операционной системы и ее собственных API. Все, что вам нужно знать, это то, что вы звоните Closable.close() в своем блоке finally, и ваш ресурс всегда будет освобожден.

...