我认为这是一个相当令人困惑的死锁示例——它增加了问题的其他噪音。
一个非常简单的例子可以通过使用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());
}
}
应用程序在执行程序中启动两个线程,然后我们让这些线程调用两个可运行对象。
Lock
这些可运行对象尝试以相反的顺序获取两个对象上的锁。
所以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
。
这种情况将永远持续下去,因此陷入僵局。
您的代码实现了相同的结果,但不是使用手动产生的两个线程,而是使用应用程序主线程(由 JVM 产生)和一个手动产生的线程。
它还使用synchronized
两个Object
s 而不是两个Lock
对象中的方法。