Как блокировка повторного входа помогает снова, когда у нас уже есть блокировка?
Таким образом, повторяющаяся блокировка увеличивает счет на единицу, если текущий поток снова получает блокировку. что я не могу понять, это почему и как это помогает или приносит пользу нам?
3 ответа
Причина, по которой это делает повторяющаяся блокировка, состоит в том, чтобы не блокировать тот же поток, который уже получил эту блокировку.
Например: Допустим, у вас есть Поток A, который получает блокировку Reentrant A. Чем Поток B пытается получить Блокировку A, что приведет к блокировке потока B (более подробную информацию о состояниях потока можно найти здесь). Теперь Поток А пытается (снова) получить Замок А.
Поскольку блокировка Reentrant теперь увеличивается, поток A не блокируется. Поток А по-прежнему имеет доступ к замку и может продолжать (в замках хранится информация о глубине). Если он (рано или поздно) снимает блокировку, счет снова будет снижен, чтобы проверить, нужна ли еще потоке А блокировка или нет. Если счетчик становится равным 0, то есть поток A освобождается при каждом получении, поток B получает доступ через замок.
Без повторного входа вы бы сейчас оказались в тупике. Зачем? Потому что поток А имеет блокировку и будет ждать ее получения снова.
Параллелизм может быть действительно сложным, а вход помогает (немного) уменьшить эту сложность.
Смысл увеличения счетчика для блокировки заключается в том, чтобы отслеживать, сколько раз поток получал блокировку, чтобы блокировка фактически не снималась до тех пор, пока поток не сообщит о готовности снять блокировку столько же раз.
Предполагается, что команды для блокировки будут сопоставлены с командами для снятия блокировки.
Скажем, мой код входит в раздел кода А, который требует блокировки.
Затем, не выходя из раздела кода A, он входит в раздел кода B, который также требует такой же блокировки. Как вы заметили, у нас есть блокировка, поэтому нам не нужно блокировать.
Но мы оставим Раздел B, и Раздел B написан для снятия блокировки при выходе из нее. (В противном случае код, который достигает Раздела B без блокировки, никогда не снимет блокировку.)
Мы все еще в Разделе A, поэтому мы не хотим по- настоящему снимать блокировку, иначе другой поток может снять ее, пока мы в Разделе A.
Поэтому, когда мы вошли в Раздел B, мы увеличили счетчик блокировок, что означает, что при выходе из Раздела B мы можем уменьшить счетчик на единицу и, увидев, что он не вернулся к 0, не освободить его.
Затем, когда Секция A снова снимает блокировку, счет возвращается к 0, и тогда мы действительно отпускаем блокировку.
Это помогает в необычной ситуации, когда вы хотите вызвать другой метод, который также требует блокировки.
ReentrantLock lock = new ReentrantLock();
public void doSomething() {
lock.lock();
try {
// Something.
} finally {
lock.unlock();
}
}
public void somethingElse () {
lock.lock();
try {
// Something else.
// We can now call another locking method without risking my lock being released.
doSomething();
} finally {
lock.unlock();
}
}
Здесь публика может позвонить doSomething
и он получит блокировку, сделает свое дело и затем снимет блокировку, когда unlock
называется.
Однако когда somethingElse
называется и вызывает doSomething
это только увеличивает количество блокировок. когда doSomething
разблокирует, он не снимает блокировку, он просто считает отсчет блокировки, оставляя окончательную разблокировку в somethingElse
снять замок.