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

"Reentrancy" в java

Reentrancy означает, что блокировки получаются на основе потока, а не на каждом вызове.

Поскольку внутренняя блокировка удерживается потоком, означает ли это, что нить, выполняемая один раз, равна основанию вызова?

Спасибо, похоже, что: в потоке, если я получаю блокировку lockA, когда функция процесса doA, которая вызывает функцию doB, и doB также нуждается в блокировке lockA, тогда будет реентерацией. В Java это явление приобретается за нить, поэтому мне не нужно учитывать взаимоблокировки?

4b9b3361

Ответ 1

Reentrancy означает, что блокировки получаются на основе потока, а не на каждом вызове.

Это вводящее в заблуждение определение. Это правда (вроде), но это не соответствует реальной точке.

Reentrancy означает (в общем, терминологию CS/IT), что вы что-то делаете, и пока вы все еще это делаете, вы делаете это снова. В случае замков это означает, что вы делаете что-то подобное в одном потоке:

  • Приобретайте блокировку на "foo".
  • Сделайте что-нибудь
  • Получить блокировку на "foo". Обратите внимание, что мы не выпустили блокировку, которую мы ранее приобрели.
  • ...
  • Блокировка блокировки на "foo"
  • ...
  • Блокировка блокировки на "foo"

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

Вот пример в Java с использованием примитивных объектов/мониторов... которые являются реентерабельными:

Object lock = new Object();
...
synchronized (lock) {
    ...
    doSomething(lock, ...)
    ...
}

public void doSomething(Object lock, ...) {
    synchronized (lock) {
        ...
    }
}

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

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


Так что мне не нужно учитывать взаимоблокировки?

Да, вы делаете.

Нить не будет зациклена на себя (если блокировка будет реентерабельной). Однако вы можете получить тупик, если есть другие потоки, которые могут блокировать объект, который вы пытаетесь заблокировать.

Ответ 2

Представьте себе что-то вроде этого:

function A():
   lock (X)
       B()
   unlock (X)

function B():
    A()

Теперь мы вызываем A. Происходит следующее:

  • Вводим A, фиксируя X
  • Вводим B
  • Мы снова вводим A, снова запираем X

Поскольку мы никогда не выходили из первого вызова A, X все еще заблокирован. Это называется повторным входом - пока функция A еще не вернулась, функция A вызывается снова. Если A полагается на какое-то глобальное статическое состояние, это может вызвать ошибку "повторного входа", где до того, как статическое состояние очищается от выхода функции, функция запускается снова, а половина вычисленных значений сталкивается с началом второй вызов.

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

В java, lock и synchronized отображаются сообщения о повторном входе - если блокировка удерживается потоком, а поток пытается повторно получить один и тот же замок, это разрешено. Поэтому, если бы мы написали вышеуказанный псевдокод в Java, это не затормозило бы.

Ответ 3

Java concurrency в практических книжных состояниях - Reentrancy means that locks are acquired on a per-thread rather than per-invocation basis.

Позвольте мне объяснить, что это значит. Прежде всего Внутренние замки являются реентерабельными по своей природе. Способ реинтеграции достигается путем поддержания счетчика количества приобретенных замков и владельца замка. Если счетчик равен 0, и владелец не связан с ним, означает, что блокировка не удерживается нитью. Когда поток получает блокировку, JVM записывает владельца и устанавливает счетчик равным 1. Если тот же поток пытается снова получить блокировку, счетчик увеличивается, а при наличии существующего потока синхронизированный счетчик блоков уменьшается. Когда счетчик достигает 0, снова блокируется.

Простым примером может быть -

public class Test {
    public synchronized void performTest() {
       //...
    }
}

public class CustomTest extends Test {
    public synchronized void performTest() {
       //...
       super.performTest();
    }
}

без повторного входа в тупик был бы тупик.

введите описание изображения здесь

Ответ 4

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

public void methodA()
{
     // other code
     synchronized(this)
     {
          methodB();
     } 
}

public void methodB()
{
     // other code
     syncrhonized(this)
     {
          // it can still enter this code    
     }

}