1. 程式人生 > >ex:併發程式設計經(1)

ex:併發程式設計經(1)

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