中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

mongo分布式鎖Java實現方法(推薦)

發布時間:2020-08-19 15:13:26 來源:腳本之家 閱讀:229 作者:jingxian 欄目:編程語言

一、分布式鎖使用場景:

代碼部署在多臺服務器上,即分布式部署。

多個進程同步訪問一個共享資源。

二、需要的技術:

數據庫:mongo

java:mongo操作插件類 MongoTemplate(maven引用),如下:

<!--mongodo開始-->
    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-mongodb</artifactId>
      <version>1.8.2.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-commons</artifactId>
      <version>1.10.0.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.mongodb</groupId>
      <artifactId>mongo-java-driver</artifactId>
      <version>2.13.0-rc2</version>
    </dependency>
 <!--mongodo結束-->

三、實現代碼:

主實現邏輯及外部調用方法,獲得鎖調用getLock,釋放鎖調用releaseLock,詳情如下:

import java.util.HashMap;
import java.util.List;
import java.util.Map;


public class MongoDistributedLock {

  static MongoLockDao mongoLockDao;

  static {
    mongoLockDao = SpringBeanUtils.getBean("mongoLockDao");
  }

  /**
   * 獲得鎖的步驟:
   * 1、首先判斷鎖是否被其他請求獲得;如果沒被其他請求獲得則往下進行;
   * 2、判斷鎖資源是否過期,如果過期則釋放鎖資源;
   * 3.1、嘗試獲得鎖資源,如果value=1,那么獲得鎖資源正常;(在當前請求已經獲得鎖的前提下,還可能有其他請求嘗試去獲得鎖,此時會導致當前鎖的過期時間被延長,由于延長時間在毫秒級,可以忽略。)
   * 3.2、value>1,則表示當前請求在嘗試獲取鎖資源過程中,其他請求已經獲取了鎖資源,即當前請求沒有獲得鎖;
   * !!!注意,不需要鎖資源時,及時釋放鎖資源!!!。
   *
   * @param key
   * @param expire
   * @return
   */
  public static boolean getLock(String key, long expire) {
    List<MongoLock> mongoLocks = mongoLockDao.getByKey(key);
    //判斷該鎖是否被獲得,鎖已經被其他請求獲得,直接返回
    if (mongoLocks.size() > 0 && mongoLocks.get(0).getExpire() >= System.currentTimeMillis()) {
      return false;
    }
    //釋放過期的鎖
    if (mongoLocks.size() > 0 && mongoLocks.get(0).getExpire() < System.currentTimeMillis()) {
      releaseLockExpire(key, System.currentTimeMillis());
    }
    //!!(在高并發前提下)在當前請求已經獲得鎖的前提下,還可能有其他請求嘗試去獲得鎖,此時會導致當前鎖的過期時間被延長,由于延長時間在毫秒級,可以忽略。
    Map<String, Object> mapResult = mongoLockDao.incrByWithExpire(key, 1, System.currentTimeMillis() + expire);
    //如果結果是1,代表當前請求獲得鎖
    if ((Integer) mapResult.get("value") == 1) {
      return true;
      //如果結果>1,表示當前請求在獲取鎖的過程中,鎖已被其他請求獲得。
    } else if ((Integer) mapResult.get("value") > 1) {
      return false;
    }
    return false;
  }

  /**
   * 釋放鎖
   *
   * @param key
   */
  public static void releaseLock(String key) {
    Map<String, Object> condition = new HashMap<>();
    condition.put("key", key);
    mongoLockDao.remove(condition);
  }

  /**
   * 釋放過期鎖
   *
   * @param key
   * @param expireTime
   */
  private static void releaseLockExpire(String key, long expireTime) {
    mongoLockDao.removeExpire(key, expireTime);
  }
}

MongoLockDao實現代碼:

import org.springframework.data.mongodb.core.FindAndModifyOptions;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Repository;

import java.util.HashMap;
import java.util.List;
import java.util.Map;


@Repository
public class MongoLockDao <MongoLock> {
  private Class<?> clz;

  public Class<?> getClz() {
    if (clz == null) {
      //獲取泛型的Class對象
      clz = ((Class<?>)
          (((ParameterizedType) (this.getClass().getGenericSuperclass())).getActualTypeArguments()[0]));
    }
    return clz;
  }

  /**
   * 返回指定key的數據
   *
   * @param key
   * @return
   */
  public List<MongoLock> getByKey(String key) {
    Query query = new Query();
    query.addCriteria(Criteria.where("key").is(key));
    return (List<MongoLock>) mongoTemplate.find(query, getClz());
  }


  /**
   * 指定key自增increment(原子加),并設置過期時間
   *
   * @param key
   * @param increment
   * @param expire
   * @return
   */
  public Map<String, Object> incrByWithExpire(String key, double increment, long expire) {
    //篩選
    Query query = new Query();
    query.addCriteria(new Criteria("key").is(key));

    //更新
    Update update = new Update();
    update.inc("value", increment);
    update.set("expire", expire);
    //可選項
    FindAndModifyOptions options = FindAndModifyOptions.options();
    //沒有則新增
    options.upsert(true);
    //返回更新后的值
    options.returnNew(true);
    Map<String, Object> resultMap = new HashMap<>();
    resultMap.put("value", Double.valueOf(((MongoLock)
        mongoTemplate.findAndModify(query, update, options, getClz())).getValue()).intValue());
    resultMap.put("expire", Long.valueOf(((MongoLock)
        mongoTemplate.findAndModify(query, update, options, getClz())).getExpire()).longValue());
    return resultMap;
  }


  /**
   * 根據value刪除過期的內容
   *
   * @param key
   * @param expireTime
   */
  public void removeExpire(String key, long expireTime) {
    Query query = new Query();
    query.addCriteria(Criteria.where("key").is(key));
    query.addCriteria(Criteria.where("expire").lt(expireTime));
    mongoTemplate.remove(query, getClz());
  }

  public void remove(Map<String, Object> condition) {
    Query query = new Query();
    Set<Map.Entry<String, Object>> set = condition.entrySet();
    int flag = 0;
    for (Map.Entry<String, Object> entry : set) {
      query.addCriteria(Criteria.where(entry.getKey()).is(entry.getValue()));
      flag = flag + 1;
    }
    if (flag == 0) {
      query = null;
    }
    mongoTemplate.remove(query, getClz());
  }

}

MongoLock實體:

public class MongoLock {

  private String key;
  private double value;
  private long expire;

  public double getValue() {
    return value;
  }

  public void setValue(double value) {
    this.value = value;
  }

  public long getExpire() {
    return expire;
  }

  public void setExpire(long expire) {
    this.expire = expire;
  }

  public String getKey() {
    return key;
  }

  public void setKey(String key) {
    this.key = key;
  }
}

四、設計思路

前提:利用mongo實現id自增,且自增過程為原子操作,即線程安全。

假設有A、B兩個請求通過請求資源。

當A請求到資源是調用mongo自增 +1,并將結果返回給A,即1,此時結果等于1則表明,A請求過程中沒有其他請求請求到資源,將鎖資源分配給A。

當B請求到資源是調用mongo自增 +1,并將結果返回給A,即2。此時結果大于1則表明,B請求過程中有其他請求請求到資源,鎖資源不能分配給B。

這樣就是實現了多個請求請求同一個鎖并且排隊。

關于鎖過期時間 :

如果圖中代碼1releaseLockExpire(key, System.currentTimeMillis())修改為releaseLockExpire(key),即在釋放鎖的時候沒有傳入過期時間,會產生如下情況:

A、B兩個請求同時通過條件,進入到代碼 1

B執行完刪除操作,進入代碼2,并且剛剛獲得到鎖資源,而此時A及有可能剛開始執行釋放鎖的操作。

此時就會發生,A釋放了B剛剛獲得的鎖,這樣B就會失去剛剛獲得的鎖,而B確沒有感知,從而造成邏輯錯誤。

而releaseLockExpire(key, System.currentTimeMillis()),即在釋放鎖的時候判斷一下過期時間,這樣就不會誤刪B剛剛獲得的鎖。

mongo分布式鎖Java實現方法(推薦)

以上這篇mongo分布式鎖Java實現方法(推薦)就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支持億速云。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

措美县| 东丰县| 福贡县| 土默特左旗| 六枝特区| 邢台市| 曲麻莱县| 电白县| 宣武区| 简阳市| 滨州市| 汉源县| 唐山市| 阿拉善左旗| 宣汉县| 营山县| 高陵县| 辛集市| 岳普湖县| 澎湖县| 关岭| 贵定县| 两当县| 皮山县| 佛教| 乃东县| 美姑县| 玉林市| 原阳县| 宜城市| 文安县| 息烽县| 怀来县| 松潘县| 响水县| 宜兰市| 湘乡市| 曲松县| 河北省| 长汀县| 渑池县|