检查锁是否被持有但如果它是空闲的则不锁定它
Check if lock is held but not lock it if it is free
我有多个计数器对象的场景。每个计数器对象可以同时由多个线程递增,因此所有对象都有一组 ReentrantLock,这很好用 - 每个对象在给定时刻只能由一个线程修改。
关键在于:有一个进程每 15 分钟运行一次,收集所有计数器对象,进行一些计算并清除计数器。该线程没有锁定任何东西,所以有以下情况:
- incrementing_thread 正在获取计数器对象并递增一些计数器
- clearing_thread 正在获取所有计数器对象,进行一些计算并清除计数器
- clearing_thread 将计数器对象保存到缓存
- incrementing_thread 将计数器对象保存到缓存
在这种情况下,其中一个计数器对象被弄乱了,因为最后清除操作被丢弃并且计数器的状态与清除前相同。
我想要的:
- 所有 incrementing_thread 都锁定在特定的计数器对象上,因此每个对象只能由一个线程修改,但同时独立的对象可以由多个线程修改,这项工作已经很棒了。
- 当 clearing_thread 启动时,它会设置某种标志,所有 incrementing_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”的片段,它将所有统计对象复制到本地映射,然后清除集群中的统计信息:
statisticsData.setObjStats(new HashMap<>(Cluster.getTgStatistics()));
Cluster.clearTgStatistics();
您可以使用 ReadWriteLock
.
- 递增线程在递增值之前获取读锁。
- 清理线程获取写锁。
您仍然需要为每个计数器单独锁。
我有多个计数器对象的场景。每个计数器对象可以同时由多个线程递增,因此所有对象都有一组 ReentrantLock,这很好用 - 每个对象在给定时刻只能由一个线程修改。
关键在于:有一个进程每 15 分钟运行一次,收集所有计数器对象,进行一些计算并清除计数器。该线程没有锁定任何东西,所以有以下情况:
- incrementing_thread 正在获取计数器对象并递增一些计数器
- clearing_thread 正在获取所有计数器对象,进行一些计算并清除计数器
- clearing_thread 将计数器对象保存到缓存
- incrementing_thread 将计数器对象保存到缓存
在这种情况下,其中一个计数器对象被弄乱了,因为最后清除操作被丢弃并且计数器的状态与清除前相同。
我想要的:
- 所有 incrementing_thread 都锁定在特定的计数器对象上,因此每个对象只能由一个线程修改,但同时独立的对象可以由多个线程修改,这项工作已经很棒了。
- 当 clearing_thread 启动时,它会设置某种标志,所有 incrementing_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”的片段,它将所有统计对象复制到本地映射,然后清除集群中的统计信息:
statisticsData.setObjStats(new HashMap<>(Cluster.getTgStatistics()));
Cluster.clearTgStatistics();
您可以使用 ReadWriteLock
.
- 递增线程在递增值之前获取读锁。
- 清理线程获取写锁。
您仍然需要为每个计数器单独锁。