CAS实际使用
ConcurrentHashMap是支持并发的,但是如下代码有啥问题?
private final Map<String, RateLimiter> limiterMap = new ConcurrentHashMap<>();
// 并发使用的方法
public void someMethod(){
if (!limiterMap.containsKey(limitName)) {
rateLimiter = RateLimiter.create(rateLimiterAnno.limitCount());
limiterMap.put(limitName, rateLimiter);
}
LOG.info("-----------"+limiterMap.get(limitName).hashCode());
rateLimiter = limiterMap.get(limitName);
}
如上代码从limiterMap获取到的rateLimiter在并发环境下可能并不是同一个,concurrentHashMap能保证的是每一个操作(put,get,delete…)本身是线程安全的,但是someMethod对concurrentHashMap的操作是一个组合,先get(containsKey)再put,所以多个线程的操作出现了覆盖。
那么如何防止put覆盖呢?可参考:http://www.importnew.com/26035.html
用到了CAS思想,CAS操作putIfAbsent,参考地址里用的是统计接口访问次数,即Map存储<String,Long>.
本问题解决代码:
private final Map<String, RateLimiter> limiterMap = new ConcurrentHashMap<>();
private RateLimiter rateLimiter;
if (!limiterMap.containsKey(limitName)) {
rateLimiter = RateLimiter.create(rateLimiterAnno.limitCount());
}
// 自定义方法
rateLimiter = getRateLimiter(limitName, rateLimiter);
// 解决多线程下:先get再put,多个线程的操作出现覆盖
private RateLimiter getRateLimiter(String key, RateLimiter rateLimiter) {
while (true) {
// 其他线程添加到了Map
if (limiterMap.get(key) != null) {
break;
} else {
// 添加RateLimiter到Map成功,退出循环
if (limiterMap.putIfAbsent(key, rateLimiter) == null) {
break;
}
}
}
return limiterMap.get(key);
}