【发布时间】:2020-09-23 08:08:06
【问题描述】:
我有多个计数器对象的情况。每个计数器对象可以同时由多个线程递增,因此所有对象都有一组 ReentrantLock,这很有效 - 每个对象在给定时刻只能由一个线程修改。
这里有一个问题:有一个进程每 15 分钟运行一次并收集所有计数器对象,进行一些计算并清除计数器。该线程没有锁定任何东西,所以有以下情况:
- incrementing_thread 正在获取计数器对象并递增一些计数器
- clearing_thread 正在获取所有计数器对象,进行一些计算并清除计数器
- clearing_thread 将计数器对象保存到缓存中
- incrementing_thread 将计数器对象保存到缓存中
在这种情况下,其中一个计数器对象被弄乱了,因为最后清除操作被丢弃并且计数器的状态与清除前相同。
我想要什么:
- 所有 incrementing_threads 都锁定在特定的计数器对象上,因此每个对象只能由一个线程修改,但同时独立的对象可以由多个线程修改,这项工作已经很棒了。
- 当 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();
【问题讨论】:
-
请添加代码。代码说超过 1000000 个单词。
-
我添加了代码,但同时问题已解决:)
标签: java multithreading reentrantlock