RDM
RDM

Reputation: 1191

Java concurrency - How to synchronize on method parameter

I have a question related to synchronization and concurrency in Java.

So I have a method, like this:

private boolean loadData(final Integer fundId, List<Trade> trades) {
        synchronized (fundId) {
            // do lots of things here and finally load the trades into DB
        }
    }

Before I made this change, the complete method loadData was synchronized private synchronized boolean loadData. However, my requirement is such that if, lets say, fundId - 1 is processing, then I can allow concurrent processing of any other fundId other than 1.

So, the above code also won't work because the lock would be on the Integer object, hence no other fundId can be concurrently processed. Is there a way to achieve concurrent processing based on the method parameter ?

Upvotes: 1

Views: 512

Answers (3)

Katy
Katy

Reputation: 1157

You need to create an entry in a ConcurrentHashMap for each value of fundId in order to lock it.

   static Map<Integer, Object> locks = new ConcurrentHashMap<>();
    
   private boolean loadData(final Integer fundId, List<Trade> trades){
        locks.computeIfAbsent(fundId, k-> { /* your actual job */ return null; });
        }
    }

Hope that helps !

Upvotes: 1

k1r0
k1r0

Reputation: 552

You can achieve that in several ways:

  • If the class that contains loadData() is called FundLoader you can have a Map<Integer, FundLoader> fundLoaders and each FundLoader is responsible to load the trades for given fundId. The synchronization will be again on method level for loadData
  • Do a custom synhronization inside loadData UPDATE - added fundsWaitingForLock to prevent cases when the lock is already taken from the fundLocks map
private final Map<Integer, Object> fundLocks = new HashMap<>();
private final Map<Integer, AtomicInteger> fundsWaitingForLock = new HashMap<>();

private boolean loadData(final Integer fundId, final List<String> trades) {
    Object lock;
    synchronized (fundLocks) {
        lock = fundLocks.computeIfAbsent(fundId, id -> new Object());
        fundsWaitingForLock.computeIfAbsent(fundId, id -> new AtomicInteger()).incrementAndGet();
    }
    synchronized(lock) {
        try {
            // do lots of things here and finally load the trades into DB
            return true;
        } finally {
            synchronized (fundLocks) {
                if (fundsWaitingForLock.get(fundId).decrementAndGet() == 0) {
                    fundLocks.remove(fundId);
                    fundsWaitingForLock.remove(fundId);
                }
            }
        }
    }
}
  • Pass a lock instead of fundId.
private boolean loadData(final Lock fundIdLock, final List<String> trades) {
    fundIdLock.lock();
    try {
        // do lots of things here and finally load the trades into DB
    } finally {
        fundIdLock.unlock();
    }
    return true;
}

Upvotes: -1

Burak Serdar
Burak Serdar

Reputation: 51632

The function, as it is written, will synchronize on the object fundId, not on Integer. So, it will block if you call the same function from another thread with the same fundId instance. It will not, however, synchronize if you call it with other fundId instances, regardless of the value.

If you need to synchronize based on a value, you can use a shared set of integers (i.e. fundId). Synchronize on the set, and attempt to insert the integer. If it is already in there, someone else is processing that value, so you wait. If it is not there, then you insert it, unlock, process, lock again, remove the value, and signal.

Upvotes: 1

Related Questions