誰かが私にJavaコード(疑似)の例を使ってどのように説明しReentrant lock
、deadlock
相互に関係することができますか?
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()
、呼び出し元が終了するまで呼び出すことができません。カブーム!
デッドロックは、スレッドが真になることのない条件を待機するときに発生します。
明らかなケースは、異なるスレッドによって異なる順序でロックされた2つのロックをロックしようとしている場合です。
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.
}
ご覧のとおり、スレッドがmethodAを呼び出してlock2を待機しているlock1を取得し、別のスレッドがmethodBを呼び出してlock1を待機しているlock2を取得することができます。
ただし、スレッドがそれ自体をデッドロックする可能性があります。例としては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を囲んで、calltoをコメントアウトします。
再入可能ロックにより、ロック所有者は、他のコードブロックを入力してロックを取得した後でも、コードブロックを入力できます。非再入可能ロックは、別のコードブロックから取得したロックを解放して同じロックを再取得し、ネストされたロックを必要とするコードブロックに入る必要があるため、それ自体にロックホルダーブロックがあります。
デッドロックに関する限り、保護されたコードブロックから保護されたコードブロックを呼び出す場合は、再入可能なロックが必要になります(または、自分自身を待っている間にデッドロックが発生します)。