Search code examples
javamultithreadinglocking

Java lock over object(s) with some partitioning property


I have a class with some property (partition in the example below)

class Car {
   public String partition;
   ....
}

and I have a function

private void doSomething(Car car){
   //  
     Object obj = getLock(car); // should write this function
     synchronized (obj){
          // do work here
     }

}

If I have 2 instances of the Car object

from 2 different threads the function doSomething is called. the synchronize block should sync only objects with the same "partition" value. When 2 car object have a different partition key they can both enter the block of code at the same time

I was wondering how to write the "getLock" function. Should I use some map

Map<String,Object> ? 

Is there a better way prevent 2 object with same property to enter the critical section?


Solution

  • If you definitely want to use locking, I would have a look at a striped lock. Some pseudo-code:

    class CarManager{
        final Object[] lockStripe = new Object[Runtime.availableProcessors()*4];
    
         public CarManager(){
            for(int k=0;k<lockStripe.length;k++){
               lockStripe[k]=new Object();
            }
         }
    
         Object getLock(Car car){
             int hash = car.partition.hashCode();
             int lockIndex;
        
               if (hash == Integer.MIN_VALUE) {
                  lockIndex = 0;
               } else {
                   lockIndex = abs(hash) % lockStripe.length;
               }
          
               return lockStripe[lockIndex];
         }
    
         public void doSomething(Car car){
             synchronized(getLock(car)){
                ...
             }
         }
    

    This approach will ensure that:

    1. cars in the same partition will end up with the same lock.
    2. cars in different partitions have a low probability of ending up with the same lock. And you can make a trade-off between memory utilization and contention by changing the length of the lockStripe.
    3. no global lock is acquired
    4. no locks on objects you don't own like locking on the partition object.
    5. no memory leaks due to retaining locks of objects that do not exist any longer.
    6. no data races