Invoking a synchronized method of the object while aquiring ReentrantReadWriteLock.ReadLock on another thread

151 Views Asked by At

Thanks for considering my question, which I think is actually asking:

Not quite sure how exactly below code can deadlock.


The structure roughly looks like this, which has 2 classes :

  • The main class - Worker which has synchronized method
  • A util class which has class level synchronized method

The run method of Worker will deadlock and making doSomething() not synchronized can avoid it.


The thread dump of the real application shows that :

the "Worker" thread is BLOCKED - waiting on monitor lock
--> at int v = Utils.getIntValue();

the "schedule" thread is WAITING - Thread State Details here
--> at (internal code of ReentrantReadWriteLock) after 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;
    }
}

I think it is due to the static synchronized method scheduleTask locking the whole Utils class and then before the thread from ExecutorService can aquire the ReadLock of the ReentrantReadWriteLock inside Worker, another method of Worker doSomething() locked on the Worker object (intrinsic lock) hence 2 threads are waiting on each other.


I could be wrong totally but if not wrong so far I am not sure how this instance of ReentrantReadWriteLock knows the Worker instance referencing it is locked by another thread ?

It seems that when the scheduled Runnable task is running the Utils class is still locked even after scheduleTask method has returned ?



Updated the scheduleTask to use 0 second delay to be closer to the actual scenario.


Thank you for your time considering this question !

1

There are 1 best solutions below

0
On

It truns out there were another synchronized method of the "Worker" called from the schedule thread and there are methods from superclasses of "Worker" locked on the ReentrantReadWriteLock.WriteLock() ...

"Worker" thread holds the WriteLock and waiting on monitor lock

"schedule" thread holds Worker object monitor lock and waiting on ReadLock