ex:併發程式設計經(1)
阿新 • • 發佈:2018-12-04
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); }