【问题标题】:Invoking a synchronized method of the object while aquiring ReentrantReadWriteLock.ReadLock on another thread在另一个线程上获取 ReentrantReadWriteLock.ReadLock 时调用对象的同步方法
【发布时间】: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


【解决方案1】:

事实证明,“Worker”的另一个 synchronized 方法是从 schedule 线程调用的,并且“Worker”的超类中的一些方法被锁定在 ReentrantReadWriteLock.WriteLock()...

“Worker”线程持有WriteLock并等待监视器锁

“schedule”线程持有 Worker 对象监视器锁并等待ReadLock

【讨论】:

    猜你喜欢
    • 2014-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-02
    • 1970-01-01
    相关资源
    最近更新 更多