Reputation: 26870
Can someone explain to me how Reentrant lock
and deadlock
relate to each other with Java code (pseudo) example?
Upvotes: 8
Views: 13828
Reputation: 533680
A deadlock occurs when a thread waits for a condition which will never become true.
The obvious case is when you are trying to lock two locks, locked in a different order by different threads.
ReentrantLock lock1 = new ReentrantLock();
ReentrantLock lock2 = new ReentrantLock();
public void methodA() {
lock1.lock();
lock2.lock();
// do something and unlock both.
}
public void methodB() {
lock2.lock();
lock1.lock();
// do something and unlock both.
}
As you can see it is possible for a thread to call methodA and obtain lock1 waiting for lock2, and another thread to call methodB and obtain lock2 waiting for lock1.
However, it's possible for a thread to deadlock itself. An example is ReentrantReadWriteLock
because it doesn't support upgrading a read lock to a write lock.
ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
rwl.readLock().lock();
// do we need to update?
rwl.writeLock().lock(); // will wait for the readLock() to be released!
An obscure opportunity to deadlock yourself is when implied locks are used. A static initialiser block is implicitly thread-safe so a lock is used even though static initialiser blocks are not 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();
}
}
Again you have a deadlock!
Upvotes: 6
Reputation: 12006
Here's an example of deadlock with 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();
}
}
To resolve deadlock, comment out call to t.join
, along with enclosing try/catch.
Upvotes: 1
Reputation: 70929
A reentrant lock will allow the lock holder to enter blocks of code even after it has already obtained the lock by entering other blocks of code. A non-reentrant lock would have the lock holder block on itself as it would have to release the lock it obtained from another block of code to reobtain that same lock to enter the nested lock requiring block of code.
As far as deadlock is concerned, if you call a protected block of code from a protected block of code, you'll want a reentrant lock (or you will deadlock while waiting on yourself).
Upvotes: 0
Reputation: 10843
A reentrant locking mechanism allows the thread holding the lock to re-enter a critical section. This means that you can do something like this:
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!
}
In a non-reentrant lock, you would have a deadlock situation when you try to call functionTwo()
from functionOne()
because the thread would have to wait for the lock...which it holds itself.
Deadlock, of course, is the evil situation in which Thread 1 holds lock A and is waiting for lock B while Thread 2 holds lock B and is waiting for lock A. Thus, neither can continue. This code sample creates a deadlock:
public synchronized void deadlock() throws InterruptedException {
Thread th = new Thread() {
public void run() {
deadlock();
}
}.start();
th.join();
}
The calling thread tries to wait around for the spawned thread, which in turn can't call deadlock()
until the caller has exited. Ka-boom!
Upvotes: 15