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 !
It truns out there were another
synchronized
method of the "Worker" called from theschedule
thread and there are methods from superclasses of "Worker" locked on theReentrantReadWriteLock.WriteLock()
..."Worker" thread holds the
WriteLock
and waiting on monitor lock"schedule" thread holds Worker object monitor lock and waiting on
ReadLock