检查是否已持有锁定,但如果没有锁定则不锁定它

问题描述

我有多个计数器对象的情况。每个计数器对象可以同时由多个线程递增,因此所有对象都有一组reentrantlock,而且效果很好-每个对象在给定时刻只能由一个线程修改

这很重要:有一个过程每15分钟运行一次,收集所有计数器对象,进行一些计算并清除计数器。该线程没有锁定任何东西,因此存在以下情况:

  1. incrementing_thread正在获取计数器对象并增加一些计数器
  2. clearing_thread正在获取所有计数器对象,进行一些计算并清除计数器
  3. clearing_thread将计数器对象保存到缓存
  4. incrementing_thread将计数器对象保存到缓存

在这种情况下,由于最后清除操作被丢弃并且计数器的状态与清除之前的状态相同,所以计数器对象之一被弄乱了。

我想做什么:

  • 所有incrementing_threads都锁定在特定的计数器对象上,因此每个对象只能由一个线程修改,但同时独立的对象可以由多个线程修改,并且这项工作已经很不错了。
  • 当clearing_thread启动时,它会设置某种标志,所有增量线程都会读取这些标志,并且它们必须等待直到该标志被取消。

我有备份计划:

  • clearing_thread锁定所有对象,但是我不喜欢这个想法,因为它可能花费太长时间,并且如果它阻塞在一个对象上,则可能会阻塞所有线程。
  • 我可以为每个对象清除for循环中的计数器,但是然后在清除一个对象时可以修改其他对象,这对我来说并不理想。

您可以看到我有一些选择,但是我想知道是否有更好的方法可以做到这一点。

更新 我被要求提供代码,就在那里。

下面是增加对象上计数器的方法之一的示例。

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()和unlock()方法

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();
    }
}

getStatisticsForObj()和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);
}

下面是“ clearing_thread”的片段,该片段将所有统计信息对象复制到本地地图,然后清除Cluster中的统计信息:

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

解决方法

您可以使用ReadWriteLock

  • 递增线程在递增值之前获取读锁定。
  • 清洁线程获得写锁定。

每个计数器仍然需要单独的锁。