mongo分布式鎖Java實現,mongojava
一、分布式鎖使用情境:二、需要的技術:
1 <!--mongodo開始--> 2 <dependency> 3 <groupId>org.springframework.data</groupId> 4 <artifactId>spring-data-mongodb</artifactId> 5 <version>1.8.2.RELEASE</version> 6 </dependency> 7 <dependency> 8 <groupId>org.springframework.data</groupId> 9 <artifactId>spring-data-commons</artifactId>10 <version>1.10.0.RELEASE</version>11 </dependency>12 <dependency>13 <groupId>org.mongodb</groupId>14 <artifactId>mongo-java-driver</artifactId>15 <version>2.13.0-rc2</version>16 </dependency>17 <!--mongodo結束-->
View Code 三、實現代碼:
主實現邏輯及外部調用方法,獲得鎖調用getLock,釋放鎖調用releaseLock,詳情如下:
1 import java.util.HashMap; 2 import java.util.List; 3 import java.util.Map; 4 5 6 public class MongoDistributedLock { 7 8 static MongoLockDao mongoLockDao; 9 10 static {11 mongoLockDao = SpringBeanUtils.getBean("mongoLockDao");12 }13 14 /**15 * 獲得鎖的步驟:16 * 1、首先判斷鎖是否被其他請求獲得;如果沒被其他請求獲得則往下進行;17 * 2、判斷鎖資源是否到期,如果到期則釋放鎖資源;18 * 3.1、嘗試獲得鎖資源,如果value=1,那麼獲得鎖資源正常;(在當前請求已經獲得鎖的前提下,還可能有其他請求嘗試去獲得鎖,此時會導致當前鎖的到期時間被延長,由於延長時間在毫秒級,可以忽略。)19 * 3.2、value>1,則表示當前請求在嘗試擷取鎖資源過程中,其他請求已經擷取了鎖資源,即當前請求沒有獲得鎖;20 * !!!注意,不需要鎖資源時,及時釋放鎖資源!!!。21 *22 * @param key23 * @param expire24 * @return25 */26 public static boolean getLock(String key, long expire) {27 List<MongoLock> mongoLocks = mongoLockDao.getByKey(key);28 //判斷該鎖是否被獲得,鎖已經被其他請求獲得,直接返回29 if (mongoLocks.size() > 0 && mongoLocks.get(0).getExpire() >= System.currentTimeMillis()) {30 return false;31 }32 //釋放到期的鎖33 if (mongoLocks.size() > 0 && mongoLocks.get(0).getExpire() < System.currentTimeMillis()) {34 releaseLockExpire(key, System.currentTimeMillis());35 }36 //!!(在高並發前提下)在當前請求已經獲得鎖的前提下,還可能有其他請求嘗試去獲得鎖,此時會導致當前鎖的到期時間被延長,由於延長時間在毫秒級,可以忽略。37 Map<String, Object> mapResult = mongoLockDao.incrByWithExpire(key, 1, System.currentTimeMillis() + expire);38 //如果結果是1,代表當前請求獲得鎖39 if ((Integer) mapResult.get("value") == 1) {40 return true;41 //如果結果>1,表示當前請求在擷取鎖的過程中,鎖已被其他請求獲得。42 } else if ((Integer) mapResult.get("value") > 1) {43 return false;44 }45 return false;46 }47 48 /**49 * 釋放鎖50 *51 * @param key52 */53 public static void releaseLock(String key) {54 Map<String, Object> condition = new HashMap<>();55 condition.put("key", key);56 mongoLockDao.remove(condition);57 }58 59 /**60 * 釋放到期鎖61 *62 * @param key63 * @param expireTime64 */65 private static void releaseLockExpire(String key, long expireTime) {66 mongoLockDao.removeExpire(key, expireTime);67 }68 }View Code
MongoLockDao實現代碼:
1 import org.springframework.data.mongodb.core.FindAndModifyOptions; 2 import org.springframework.data.mongodb.core.query.Criteria; 3 import org.springframework.data.mongodb.core.query.Query; 4 import org.springframework.data.mongodb.core.query.Update; 5 import org.springframework.stereotype.Repository; 6 7 import java.util.HashMap; 8 import java.util.List; 9 import java.util.Map;10 11 12 @Repository13 public class MongoLockDao <MongoLock> {14 private Class<?> clz;15 16 public Class<?> getClz() {17 if (clz == null) {18 //擷取泛型的Class對象19 clz = ((Class<?>)20 (((ParameterizedType) (this.getClass().getGenericSuperclass())).getActualTypeArguments()[0]));21 }22 return clz;23 }24 25 /**26 * 返回指定key的資料27 *28 * @param key29 * @return30 */31 public List<MongoLock> getByKey(String key) {32 Query query = new Query();33 query.addCriteria(Criteria.where("key").is(key));34 return (List<MongoLock>) mongoTemplate.find(query, getClz());35 }36 37 38 /**39 * 指定key自增increment(原子加),並設定到期時間40 *41 * @param key42 * @param increment43 * @param expire44 * @return45 */46 public Map<String, Object> incrByWithExpire(String key, double increment, long expire) {47 //篩選48 Query query = new Query();49 query.addCriteria(new Criteria("key").is(key));50 51 //更新52 Update update = new Update();53 update.inc("value", increment);54 update.set("expire", expire);55 //可選項56 FindAndModifyOptions options = FindAndModifyOptions.options();57 //沒有則新增58 options.upsert(true);59 //返回更新後的值60 options.returnNew(true);61 Map<String, Object> resultMap = new HashMap<>();62 resultMap.put("value", Double.valueOf(((MongoLock)63 mongoTemplate.findAndModify(query, update, options, getClz())).getValue()).intValue());64 resultMap.put("expire", Long.valueOf(((MongoLock)65 mongoTemplate.findAndModify(query, update, options, getClz())).getExpire()).longValue());66 return resultMap;67 }68 69 70 /**71 * 根據value刪除到期的內容72 *73 * @param key74 * @param expireTime75 */76 public void removeExpire(String key, long expireTime) {77 Query query = new Query();78 query.addCriteria(Criteria.where("key").is(key));79 query.addCriteria(Criteria.where("expire").lt(expireTime));80 mongoTemplate.remove(query, getClz());81 }82 83 public void remove(Map<String, Object> condition) {84 Query query = new Query();85 Set<Map.Entry<String, Object>> set = condition.entrySet();86 int flag = 0;87 for (Map.Entry<String, Object> entry : set) {88 query.addCriteria(Criteria.where(entry.getKey()).is(entry.getValue()));89 flag = flag + 1;90 }91 if (flag == 0) {92 query = null;93 }94 mongoTemplate.remove(query, getClz());95 }96 97 }View Code
MongoLock實體:
1 public class MongoLock { 2 3 private String key; 4 private double value; 5 private long expire; 6 7 public double getValue() { 8 return value; 9 }10 11 public void setValue(double value) {12 this.value = value;13 }14 15 public long getExpire() {16 return expire;17 }18 19 public void setExpire(long expire) {20 this.expire = expire;21 }22 23 public String getKey() {24 return key;25 }26 27 public void setKey(String key) {28 this.key = key;29 }30 }View Code四、設計思路
前提:利用mongo實現id自增,且自增過程為原子操作,即安全執行緒。
關於鎖到期時間 :
如果圖中代碼1releaseLockExpire(key, System.currentTimeMillis())修改為releaseLockExpire(key),即在釋放鎖的時候沒有傳入到期時間,會產生如下情況:
致謝:感謝您的閱讀!轉載請加原文連結,謝謝!http://www.cnblogs.com/faaidong/p/7126716.html