欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

mongo分布式锁Java实现方法(推荐)

程序员文章站 2023-11-26 23:14:16
一、分布式锁使用场景: 代码部署在多台服务器上,即分布式部署。 多个进程同步访问一个共享资源。 二、需要的技术: 数据库:mongo java:mongo操作插件...

一、分布式锁使用场景:

代码部署在多台服务器上,即分布式部署。

多个进程同步访问一个共享资源。

二、需要的技术:

数据库: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实现方法(推荐)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。