Выполняется ли блок finally, если поток, выполняющий функцию, прерван?
Если у меня есть функция с секцией try/finally, и поток, выполняющий ее, прерывается в блоке try, будет ли блок finally завершен до того, как произойдет фактическое прерывание?
Ответы
Ответ 1
Согласно учебникам Java, "если поток, выполняющий код try
или catch
, прерван или убит, finally
может не выполняться, даже если приложение в целом продолжается."
Здесь полный отрывок:
Блок finally
всегда выполняется, когда блок try
завершается. Эта гарантирует, что блок finally
выполняется, даже если неожиданное исключение. Но finally
полезен не только для исключения обработка - это позволяет программисту избежать кода очистки случайно обходятся return
, continue
или break
. Помещение очистки код в блоке finally
всегда является хорошей практикой, даже если нет исключения ожидаются.
Примечание. Если JVM завершает выполнение кода try
или catch
, тогда блок finally
может не выполняться. Аналогично, если выполнение потока код try
или catch
прерван или убит, блок finally
может не выполняются, даже если приложение в целом продолжается.
class Thread1 implements Runnable {
@Override
public void run() {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("finally executed");
}
}
}
...
t1.start();
t1.interrupt();
Он печатает - наконец выполнен
Ответ 2
Прерывание потока в Java просто устанавливает флаг. Это не вызывает ничего особенного в том, что происходит с выполнением кода или влияет на поток управления.
Если ваш поток задействован или пытается ввести операцию, которая генерирует InterruptedException, тогда исключение вызывается из того места, где этот метод вызывается, и если он находится внутри блока try, то, наконец, выполняется до исключения как обычно.
Ответ 3
Многие из учебных пособий Oracle Java полезны (у меня есть ответы, ссылающиеся на страницу охраняемых блоков и введение SAX), но они не обязательно являются авторитетными, а некоторые из них имеют ошибки или являются неполными. Цитата, указанная в вопросе, препятствует прерыванию с выходом JVM, что запутывает.
Во-первых, прерывание потока в Java не имеет ничего общего с прерываниями на уровне ОС. Совместное использование имени создает возможности для путаницы, но нет связи.
Далее, выход JVM, очевидно, убивает поток без возможности выполнить любую очистку. Если процесс замирает до того, как поток дошел до блока finally, слишком плохо. Но нет никакого сравнения с прерыванием. Ничего о прерывании не позволяет окончательно завершить блокировки.
Принцип прерывания заключается в том, что действие на прерывание требует взаимодействия нити, которая прерывается. Прерывание потока отвечает по своему усмотрению, прерывание не заставляет поток ничего делать. Все вызовы Thread # interrupt() устанавливают флаг в потоке. Блокирующие методы, такие как wait или sleep, проверяют флаг, чтобы увидеть, должны ли они просыпаться раньше. (InterruptedException - это проверенное исключение, поэтому вы можете указать, кто его выдает, и ваш Runnable может его планировать.) Также любой код может использовать Thread # isInterrupted(), чтобы проверить, установлен ли его поток установленным флагом.
Когда Thread # sleep() распознает, что прерванный флаг установлен, он очищает флаг перед тем, как бросать InterruptedException. Когда ваш поток захватывает InterruptedException, хорошие манеры восстанавливают флаг с помощью Thread.currentThread(). Interrupt(), на всякий случай, если в этом потоке есть какой-либо другой код, который должен знать о прерывании. Это вступает в игру, когда у вас более сложные ситуации с вложенными синхронизаторами, где, например, какой-то глубоко вложенный компонент может прерывать свой сон, позволяя ему оставаться очищенным, может помешать более высоким уровням узнать о прерывании. В простом примере игрушек, подобном тому, который содержится в других ответах здесь, не имеет значения, восстановлен ли флаг или нет, ничего не проверяет его и поток завершается.
Ответ 4
В комментариях к ответу @Risadinha задал очень правильный вопрос о том, будет ли код в finally
выполняется, если мы восстанавливаем флаг прерывания внутри блока catch
, вызывая Thread.currentThread().interrupt()
.
Ниже приведен небольшой фрагмент кода для проверки:
final SomeContext context = new SomeContext();
Thread thread = new Thread() {
@Override
public void run() {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
// this code gets executed even though
// we interrupt thread in catch block.
context.value = 9;
}
}
};
thread.start();
thread.interrupt();
thread.join(); // need to wait for thread code to complete
assertEquals(context.value, 9); // values are the same!
Код класса SomeContext:
class SomeContext {
public volatile int value = 10;
}
Ответ 5
Эффект прерывания заключается в том, чтобы выбросить InterruptedException
в следующий раз при выполнении операции блокировки (на практике при следующем вызове метода, который указывает, что он может выбросить InterruptedException
), и в этот момент - как обычно - выполняется обычный поток выполнения try/catch
, который действительно выполняет блок finally
после try
и любых применимых catch
es.
Ответ 6
Он будет выполняться так же, как и с любым другим исключением из блока try, а не до прерывания.