Skip to content

Ferdinand Agyei-Yeboah

Reentrant Locks

March 16, 2022

Reentrant Locks

Reentrant locks are a good way to force mutual exclusion of resources.

Re-entrant locks are not “global” locks persay, you can have flexible locks based on arbitary criteria. You can store all the locks in a ConcurrentHashMap.

You can configure the locks so that user a and b cannot use the shared resource at the same time, and user c and d cannot use the shared resource at the same time. Note that based on that it would be fine for a and c to go together. You would just have two reentrant locks, ab would use lock 1, cd would use lock 2.

You should lock according to business criteria (lock on resource whenever calls are coming from the same customer, or for same product).

Take a submit order example, where submitting an order means decreasing the number of available product. You would not want someone to be able to submit an order, if another in progress order would make that product out of stock. In this case, it would make sense to create locks based on product ids. Whenever a product order comes in create a lock for that product id, reuse that reentrant lock when other orders for that product id come in. Take a look at the example below.

public void sampleCodeMultipleReentrantLocks(){
Integer productId = 1;
// Map of product locks. Product 1, 2, 3... - this is initialized inside constructor or somewhere one time setup, not in executing function
ConcurrentHashMap<Integer, ReentrantLock> productIdToLockMap = new ConcurrentHashMap<>();
if (!productIdToLockMap.containsKey(productId)){
productIdToLockMap.put(productId, new ReentrantLock());
}
ReentrantLock productLock = productIdToLockMap.get(productId);
productLock.lock();
try
{
//Do some work
}
catch(Exception e)
{
e.printStackTrace();
}
finally
{
productLock.unlock();
}
}

A global lock without any criteria would look like this. Obviously locks that are more specific to certain criteria would perform better than a blanket global lock like below.

public void globalLock(Long userId) {
//This would be initialized outside of function in real example.
ReentrantLock orderLock = new ReentrantLock();
orderLock.lock();
try
{
//Do some work
submitOrder(userId);
}
catch(Exception e)
{
e.printStackTrace();
}
finally
{
orderLock.unlock();
}
}

More Info

GFG: https://www.geeksforgeeks.org/reentrant-lock-java/
Types of Locks: https://www.baeldung.com/java-concurrent-locks


Software Engineering Tutorials & Best Practices