javamultithreadingreentrantlock

Check if lock is held but not lock it if it is free


I have scenario where i have multiple counters object. Each counters object can be incremented by multiple threads at the same time so there is a set of ReentrantLock for all objects and this works good - each object can be modified only by one thread at given moment.

Here's the catch: there is a process which runs every 15 minutes and collects all counters object, do some calculations and clears counters. This thread is not locking anything so there are situations as below:

  1. incrementing_thread is fetching counters object and incrementing some counters
  2. clearing_thread is fetching all counter objects, do some calculations and clears counters
  3. clearing_thread saves counters objects to cache
  4. incrementing_thread saves counters object to cache

In such situations one of the counters object is messed up because at the end clearing operation is discarded and the state of counters are same as before clearing.

What i want to:

I have backup plans:

As you can see i have some options but i'm wondering if there is better way to do this.

UPDATE I was asked for the code so there it is.

Below example of one of the methods that increments counters on object.

public void sipIncomingCall(String objName) {
    try {
        lock(objName);
        Stats stat = getStatisticsForObj(objName);
        long l = stat.getSipIncomingConnections().incrementAndGet();
        stat.getSipConnectionsSum().incrementAndGet();
        LOGGER.debug("incrementing sip incoming connections to {}, objName {}", l, objName);
        putStatisticsForObj(objName, stat);
    }finally {
        unlock(objName);
    }
}

lock() and unlock() methods:

private Map<String,ReentrantLock> locks = new ConcurrentHashMap<>();
protected void lock(String key) {
    ReentrantLock lock = locks.getOrDefault(key, new ReentrantLock());
    lock.lock();
}

protected void unlock(String key){
    ReentrantLock lock = locks.get(key);
    if(lock!=null){
        lock.unlock();
    }
}

methods getStatisticsForObj() and putStatisticsForObj():

private MgcfStats getStatisticsForObj(String tgName) {
    //get object from local cache (or hazelcast)
    return Cluster.getTgStatistics(tgName);
}

private void putStatisticsForObj(String tgName,MgcfStats stats){
    //saving to local cache and hazelcast
    Cluster.putTgStatistics(tgName,stats);
}

Below is fragment from "clearing_thread" which copies all statistics objects to local map and then clearing statistics in Cluster:

    statisticsData.setObjStats(new HashMap<>(Cluster.getTgStatistics()));
    Cluster.clearTgStatistics();

Solution

  • You may use ReadWriteLock.

    You still need individual locks for each counter.