Входящий замок и тупик с Java
Может кто-нибудь объяснить мне, как Reentrant lock
а также deadlock
связаны друг с другом примером кода Java (псевдо)?
4 ответа
Механизм повторного входа блокировки позволяет резьбе, удерживающей замок, повторно входить в критическую секцию. Это означает, что вы можете сделать что-то вроде этого:
public synchronized void functionOne() {
// do something
functionTwo();
// do something else
// redundant, but permitted...
synchronized(this) {
// do more stuff
}
}
public synchronized void functionTwo() {
// do even more stuff!
}
В нереентерируемой блокировке у вас будет тупиковая ситуация, когда вы пытаетесь позвонить functionTwo()
от functionOne()
потому что поток должен был бы ждать блокировки... которую он держит сам.
Конечно, тупик - это злая ситуация, в которой поток 1 удерживает блокировку A и ожидает блокировки B, а поток 2 удерживает блокировку B и ожидает блокировки A. Таким образом, ни один из них не может продолжаться. Этот пример кода создает тупик:
public synchronized void deadlock() throws InterruptedException {
Thread th = new Thread() {
public void run() {
deadlock();
}
}.start();
th.join();
}
Вызывающий поток пытается подождать порожденного потока, который в свою очередь не может вызвать deadlock()
пока вызывающий абонент не вышел. Ка-бум!
Возникает мертвая блокировка, тогда поток ожидает условия, которое никогда не произойдет.
Очевидный случай - когда вы пытаетесь заблокировать две блокировки, заблокированные в разном порядке разными потоками.
ReentrantLock lock1 = new ReentrantLock();
ReentrantLock lock2 = new ReentrantLock();
public void methodA() {
lock1.lock();
lock2.lock();
// do something and un lock both.
}
public void methodB() {
lock2.lock();
lock1.lock();
// do something and un lock both.
}
Как вы можете видеть, поток может вызвать метод A и получить lock1, ожидающий lock2, а другой поток вызвать метод B и получить lock2, ожидающий lock1.
Тем не менее, поток может заблокировать себя. Примером является ReentrantReadWriteLock, потому что он не поддерживает обновление блокировки чтения до блокировки записи.
ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
rwl.readLock().lock();
// do we need to update?
rwl.writeLock().lock(); // will wait for the readLock() to be released!
Непонятная возможность заблокировать себя - это использование скрытых замков. Статический блок инициализатора неявно является потокобезопасным, поэтому блокировка используется, даже если статические блоки инициализатора не synchronized
class A {
private static int VALUE;
static {
Thread t = new Thread() {
public void run() {
// waits for the A class to load.
VALUE = someLongTask();
}
};
t.start();
// waits for the thread.
t.join();
}
}
Снова у вас тупик!
Вот пример тупика с ReentrantLock
class Deadlock {
private static final ReentrantLock l1 = new ReentrantLock();
public static void main(String[] args) {
Thread t = new Thread(new Runnable() {
public void run() {
System.out.println("A Trying to lock...");
l1.lock();
System.out.println("A Locked...");
try {
Thread t = new Thread(new Runnable() {
public void run() {
System.out.println("B Trying to lock...");
l1.lock();
System.out.println("B Must not print");
try {
} finally {
System.out.println("B Trying to unlock...");
l1.unlock();
System.out.println("B Unlocked...");
}
}
});
t.start();
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
} finally {
System.out.println("A Trying to unlock...");
l1.unlock();
System.out.println("A Unlocked...");
}
}
});
t.start();
}
}
Чтобы устранить тупик, закомментируйте вызов t.join
вместе с приложением try/catch.
Повторная блокировка позволяет владельцу блокировки вводить блоки кода даже после того, как он уже получил блокировку, вводя другие блоки кода. Неповторяющаяся блокировка будет иметь блок держателя блокировки на себе, поскольку она должна будет снять блокировку, полученную из другого блока кода, чтобы повторно получить эту же блокировку для входа во вложенную блокировку, требующую блока кода.
Что касается взаимоблокировки, то если вы вызываете защищенный блок кода из защищенного блока кода, вам понадобится блокировка с повторным вводом (или вы будете блокироваться, ожидая себя).