これはデッドロックのかなり紛らわしい例だと思います。問題に他のノイズが追加されます。
Lock
次のようなオブジェクトを使用して、非常に単純な例を実現できます。
public class App {
private static final Lock LOCKA = new ReentrantLock();
private static final Lock LOCKB = new ReentrantLock();
private static final class Locker1 implements Runnable {
@Override
public void run() {
while (true) {
try {
LOCKA.lockInterruptibly();
Thread.sleep(100);
LOCKB.lockInterruptibly();
System.out.println("Locker 1 Got locks");
} catch (InterruptedException ex) {
return;
}
LOCKB.unlock();
LOCKA.unlock();
}
}
}
private static final class Locker2 implements Runnable {
@Override
public void run() {
while (true) {
try {
LOCKB.lockInterruptibly();
Thread.sleep(100);
LOCKA.lockInterruptibly();
System.out.println("Locker 2 Got locks");
} catch (InterruptedException ex) {
return;
} finally {
LOCKA.unlock();
LOCKB.unlock();
}
}
}
}
public static void main(String[] args) throws IOException {
final ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.submit(new Locker1());
executorService.submit(new Locker2());
}
}
アプリケーションは executor で 2 つのスレッドを開始し、それらのスレッドに 2 つの runnable を呼び出させます。
Lock
これらのランナブルは、逆の順序で2 つのオブジェクトのロックを取得しようとします。
したがって、Locker1
ロックLOCKA
は数ミリ秒待機します。Locker2
ロックLOCKB
して数ミリ秒待機すると、別のロックを取得しようとします。
状況は、他のスレッドがそれを解放しないLocker1
ためLOCKB
、Locker2
待機し、永遠に待機することです。LOCKA
これは、これらのスレッドのスレッド ダンプでかなり明確に確認できます。
"pool-1-thread-1" - Thread t@8
java.lang.Thread.State: WAITING
at sun.misc.Unsafe.park(Native Method)
- waiting to lock <7725204d> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) owned by "pool-1-thread-2" t@9
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:834)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:894)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1221)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:340)
at com.boris.testbench.App$Locker1.run(App.java:32)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
at java.util.concurrent.FutureTask.run(FutureTask.java:166)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:722)
Locked ownable synchronizers:
- locked <7567e1fa> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
- locked <5ad52411> (a java.util.concurrent.ThreadPoolExecutor$Worker)
"pool-1-thread-2" - Thread t@9
java.lang.Thread.State: WAITING
at sun.misc.Unsafe.park(Native Method)
- waiting to lock <7567e1fa> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) owned by "pool-1-thread-1" t@8
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:834)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:894)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1221)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:340)
at com.boris.testbench.App$Locker2.run(App.java:51)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
at java.util.concurrent.FutureTask.run(FutureTask.java:166)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:722)
Locked ownable synchronizers:
- locked <7725204d> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
- locked <6856c528> (a java.util.concurrent.ThreadPoolExecutor$Worker)
pool-1-thread-1
が所有するロックのロックを必要としており、 が所有するロックのロックをpool-1-thread-2
必要としていることがわかります。pool-1-thread-2
pool-1-thread-1
この状況は永遠に続くため、デッドロックが発生します。
コードは同じ結果を達成しますが、手動で生成された 2 つのスレッドを使用する代わりに、(JVM によって生成された) アプリケーションのメイン スレッドと手動で生成された 1 つのスレッドを使用します。
また、2 つのオブジェクトではなくsynchronized
2 つの s でメソッドを使用します。Object
Lock