Locks In Java [ Part — 4] : Abstract Ownable Synchronizers

Avinashsoni
5 min readJan 30, 2024

--

In this Article we are going to cover up a very intresting concept related to Abstract Ownable Synchronizers in Java [ AOS].

So AOS is a abstract class in java which help us to create locks and synchronizers in java.

  • it implements the serializable interface in java
  • it has a protected constructor which is used by the subclasses
  • it contains a single element under it which is a transient thread named as ownableExclusiveThread.

Transient

transient is a reserved keyword in java which is used to indicate if that. member of the classes needs not to be serialized if this keyword is provided.

using transient help us to save our methods not to be persisted / passed on to streams while serialization.

Now the reason why we have a transient thread in the AOS class is because we dont want any other external resource except JVM to get information about this thread.

Abstract Ownable Synchronizer

In Java , For each thread in Java we have a list of Synchronizers Attached to the Thread.

Ownable Synchronizers are the ones for which the thread has acquired a lock.

Internally when we call the thread class lock method it internally calls a lock method of the Sync Class in Java ( which extends the Abstract Ownable Synchronizers) .

Once the Lock is Acquired Successfully , the acquired lock is added to the Locked Ownable Synchronizers List of the thread.

Usage

AOS is very useful in identifying the deadlocks in any application.

lets take an example and understand how it works:

package org.example.semaphores;

import java.util.concurrent.locks.ReentrantLock;

public class DeadLockTest {
public static void main(String[] args) throws InterruptedException {
ReentrantLock lock1= new ReentrantLock(true);
ReentrantLock lock2 = new ReentrantLock(true);

Thread first = createThread("first",lock1,lock2);
Thread second = createThread("second",lock2,lock1);

first.start();
second.start();
first.join();
second.join();
}

private static Thread createThread(String threadName, ReentrantLock lock1,
ReentrantLock lock2) {
return new Thread(() -> {
System.out.println("thread Being Created = " + threadName );
lock1.lock();
System.out.println( threadName + " lock is Acquired on top ");
synchronized (DeadLockTest.class){
System.out.println( threadName + " entered the
critical Section ");
DeadLockTest.class.notify();

if(!lock2.isLocked()){

try {
DeadLockTest.class.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}

}

lock2.lock();
System.out.println( threadName + " second lock is
acquired in critical section ");
}

System.out.println(threadName + "completed");
lock1.unlock();
lock2.unlock();
});
}
}

Here we have created two locks with fairness parameter ( Reentrant Locks).

now we are creating two threads and we are passing the locks and we are acquiring the first lock -> entering the critical section -> if the other lock is not acquired we go into a wait state , other wise we acquire the lock.

now we create these two threads and run them and then join them.

lets see the output produced by the above code :

Here you can see that when both the threads run the locks are already acquired by the threads on each other which results in a dead lock scenario.

Now we will see the thread Dump of this class and then we will see how Abstract Ownable Synchronizers are helping us to find the deadlock.

Steps to Print Thread Dump

  1. PID identification:

we first need to identify the PID of the class.

for this go to the terminal and print

jps

you can see the PID for DeadLock Test class is = 3861

2. Printing Using jstack

Now we print the thread Dump using the jstack command

jstack <PID> [ add your PID here ] 

now lets see the dump of the two threads we have created here :

thread — 0 dump :

"Thread-0" #13 prio=5 os_prio=31 cpu=0.56ms elapsed=275.55s tid=0x000000012a8b3000 nid=0x6203 waiting on condition  [0x000000017045e000]
java.lang.Thread.State: WAITING (parking)
at jdk.internal.misc.Unsafe.park(java.base@11.0.20.1/Native Method)
- parking to wait for <0x0000000747ee9800> (a java.util.concurrent.locks.ReentrantLock$FairSync)
at java.util.concurrent.locks.LockSupport.park(java.base@11.0.20.1/LockSupport.java:194)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(java.base@11.0.20.1/AbstractQueuedSynchronizer.java:885)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(java.base@11.0.20.1/AbstractQueuedSynchronizer.java:917)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(java.base@11.0.20.1/AbstractQueuedSynchronizer.java:1240)
at java.util.concurrent.locks.ReentrantLock.lock(java.base@11.0.20.1/ReentrantLock.java:267)
at org.example.semaphores.DeadLockTest.lambda$createThread$0(DeadLockTest.java:36)
- locked <0x0000000747ee5d88> (a java.lang.Class for org.example.semaphores.DeadLockTest)
at org.example.semaphores.DeadLockTest$$Lambda$14/0x0000000800066840.run(Unknown Source)
at java.lang.Thread.run(java.base@11.0.20.1/Thread.java:829)

thread — 1 dump:

"Thread-1" #14 prio=5 os_prio=31 cpu=0.30ms elapsed=275.55s tid=0x000000012a8b6000 nid=0x7403 in Object.wait()  [0x000000017066a000]
java.lang.Thread.State: BLOCKED (on object monitor)
at java.lang.Object.wait(java.base@11.0.20.1/Native Method)
- waiting on <0x0000000747ee5d88> (a java.lang.Class for org.example.semaphores.DeadLockTest)
at java.lang.Object.wait(java.base@11.0.20.1/Object.java:328)
at org.example.semaphores.DeadLockTest.lambda$createThread$0(DeadLockTest.java:29)
- waiting to re-lock in wait() <0x0000000747ee5d88> (a java.lang.Class for org.example.semaphores.DeadLockTest)
at org.example.semaphores.DeadLockTest$$Lambda$14/0x0000000800066840.run(Unknown Source)
at java.lang.Thread.run(java.base@11.0.20.1/Thread.java:829)

ok now if you see the two dumps then in the first dump [ thread — 0 ]

see

see the id here -> 0x0000000747ee5d88

now this is the object which is stored the list of Ownable Synchronizers for the thread — 2

and from thread — 1 we see :

so here from dumps you can see that

  • thread — 0 has locked the object 0x0000000747ee5d88
  • thread — 1 is waiting for this object to acquire the lock

so here it is a dead lock scenario and now we know that to release the lock we need to interrupt or call release on the thread — 0 acquired lock so that then thread — 1 can get the lock and deadlock can be released.

now we will further see how this AOS is being used by other synchronization classes which in turn help us to create locking mechanisms in java.

code : https://github.com/avinashsoni9829/Threading/blob/main/locks/DeadLockDetection.java

Thanks for reading !! 😁

--

--

Avinashsoni

SDE@BNY MELLON | Spring Boot | Learning and Growing EveryDay 😁 Linkedln 👇: https://www.linkedin.com/in/asoni93/