【发布时间】:2021-06-03 20:49:43
【问题描述】:
感谢您考虑我的问题,我认为这实际上是在问:
不太清楚下面的代码究竟是如何死锁的。
结构大致是这样的,有2个类:
- 主类 - Worker 有同步方法
- 具有类级同步方法的util类
Worker的run方法会死锁,使doSomething()不同步可以避免。
真实应用的线程转储显示:
“Worker”线程被阻塞 - 等待监视器锁定
--> 在 int v = Utils.getIntValue();
“计划”线程正在等待 - Thread State Details here
--> 在(ReentrantReadWriteLock的内部代码)后readWriteLock.readLock().lock();
at jdk.internal.misc.Unsafe.park(Native Method)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:194)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:885)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireShared(AbstractQueuedSynchronizer.java:1009)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireShared(AbstractQueuedSynchronizer.java:1324)
public class Worker {
ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public String getData() {
try{
readWriteLock.readLock().lock();
return "Data";
} finally {
readWriteLock.readLock().unlock();
}
}
public synchronized void doSomething() {
// blocked due to the invocation of Utils.scheduleTask
int v = Utils.getIntValue();
System.out.println(v);
}
public void run() {
// A static method which creates a thread that accesses the ReadWriteLock
Utils.scheduleTask(this::getData);
// some other quick tasks in between
// ...
// then enter this synchronized method
doSomething();
}
}
public class Utils {
static synchronized void scheduleTask(Runnable task) {
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1, r -> {
Thread t = Executors.defaultThreadFactory().newThread(r);
t.setName("schedule");
return t;
});
executorService.schedule(task, 0L, TimeUnit.SECONDS);
}
static int getIntValue() {
return 123;
}
}
我认为这是由于静态同步方法scheduleTask锁定了整个Utils类,然后在来自ExecutorService的线程可以获取ReentrantReadWriteLock内部的ReadLock Worker,另一种 Worker doSomething() 锁定在 Worker 对象上的方法(内在锁),因此 2 个线程正在相互等待。
我可能完全错了,但如果到目前为止没有错,我不确定ReentrantReadWriteLock 的这个实例是如何知道引用它的 Worker 实例被另一个线程锁定的?
似乎在预定的Runnable task 运行时,即使在scheduleTask 方法返回后,Utils 类仍然被锁定?
更新了scheduleTask 使用 0 秒延迟以更接近实际情况。
感谢您抽出宝贵时间考虑这个问题!
【问题讨论】:
-
你有一些疯狂的设计,但似乎没有任何死锁。您有 3 个锁/监视器和 2 个线程(大概),它们没有共享它们。
-
@Kayaman 感谢您查看这个问题,这是随着时间的推移而演变的实际复杂代码库的简化版本。线程转储显示主线程 - Worker - 处于 BLOCKED 状态,即等待监视器锁定(可能是 Utils 类对象的监视器?),而“schedule”线程处于 WAITING 状态并使“doSomething()”不同步使事情移动向前。我只是想找出确切的原因
-
那么看看不同线程持有的锁,看看哪些在竞争锁。这不是猜测,这是科学。
标签: java deadlock static-methods synchronized reentrantreadwritelock