你的位置:首页 > 数据库

[数据库]Jedis分布式锁实现


package com.wdzj.redis.distributed.lock;import java.util.UUID;import redis.clients.jedis.Jedis;import redis.clients.jedis.JedisPool;import redis.clients.jedis.exceptions.JedisException;/** * Jedis分布式锁实现 * * @author wuwenyu * */public class JedisLock {  private static final Lock NO_LOCK = new Lock(new UUID(0l, 0l), 0l);  private static final int ONE_SECOND = 1000;  // 锁过期时间  public static final int DEFAULT_EXPIRY_TIME_MILLIS = 60 * ONE_SECOND;  private final int lockExpiryInMillis;  // 等待获取锁时间  public static final int DEFAULT_ACQUIRE_TIMEOUT_MILLIS = 10 * ONE_SECOND;  private final int acquiryTimeoutInMillis;  // 过获取过程中的间隔时间  public static final int DEFAULT_ACQUIRY_RESOLUTION_MILLIS = 100;  // jedis连接池  private final JedisPool pool;  // 加锁路径  private final String lockKeyPath;  // 抓取锁UUID 锁标识  private final UUID lockUUID;  private Lock lock = null;  public JedisLock(JedisPool pool, String lockKey) {    this(pool, lockKey, DEFAULT_ACQUIRE_TIMEOUT_MILLIS,        DEFAULT_EXPIRY_TIME_MILLIS);  }  public JedisLock(JedisPool pool, String lockKey, int acquireTimeoutMillis) {    this(pool, lockKey, acquireTimeoutMillis, DEFAULT_EXPIRY_TIME_MILLIS);  }  public JedisLock(JedisPool pool, String lockKey, int acquireTimeoutMillis,      int expiryTimeMillis) {    this.pool = pool;    this.lockKeyPath = lockKey;    this.acquiryTimeoutInMillis = acquireTimeoutMillis;    this.lockExpiryInMillis = expiryTimeMillis + 1;    this.lockUUID = UUID.randomUUID();  }  /**   * 获取redis分布式锁   * @return   * @throws Exception   */  public synchronized boolean acquire() throws Exception {    Jedis jedis = null;    try {      jedis = pool.getResource();      int timeout = acquiryTimeoutInMillis;      while (timeout >= 0) {        final Lock newLock = new Lock(lockUUID,            System.currentTimeMillis() + lockExpiryInMillis);        if (jedis.setnx(lockKeyPath, newLock.toString()) == 1) {          this.lock = newLock;          return true;        }        final String currentValueStr = jedis.get(lockKeyPath);        final Lock currentLock = Lock.fromString(currentValueStr);        if (currentLock.isExpiredOrMine(lockUUID)) {          String oldValueStr = jedis.getSet(lockKeyPath,              newLock.toString());          if (oldValueStr != null              && oldValueStr.equals(currentValueStr)) {            this.lock = newLock;            return true;          }        }        timeout -= DEFAULT_ACQUIRY_RESOLUTION_MILLIS;        Thread.sleep(DEFAULT_ACQUIRY_RESOLUTION_MILLIS);      }    } catch (JedisException e) {      if (jedis != null) {        pool.returnBrokenResource(jedis);      }    } finally {      if (jedis != null) {        pool.returnResource(jedis);      }    }    return false;  }  /**   * 释放redis分布式锁   * @throws Exception   */  public synchronized void release() throws Exception{    if (this.lock != null) {      Jedis jedis = null;      try {        jedis = pool.getResource();        jedis.del(lockKeyPath);      } catch (JedisException e) {        if (jedis != null) {          pool.returnBrokenResource(jedis);        }      } finally {        if (jedis != null) {          pool.returnResource(jedis);        }        this.lock = null;      }          }  }  protected static class Lock {    private UUID uuid;    private long expiryTime;    protected Lock(UUID uuid, long expiryTimeInMillis) {      this.uuid = uuid;      this.expiryTime = expiryTimeInMillis;    }    protected static Lock fromString(String text) {      try {        String[] parts = text.split(":");        UUID theUUID = UUID.fromString(parts[0]);        long theTime = Long.parseLong(parts[1]);        return new Lock(theUUID, theTime);      } catch (Exception any) {        return NO_LOCK;      }    }    public UUID getUUID() {      return uuid;    }    public long getExpiryTime() {      return expiryTime;    }    @Override    public String toString() {      return uuid.toString() + ":" + expiryTime;    }    boolean isExpired() {      return getExpiryTime() < System.currentTimeMillis();    }    boolean isExpiredOrMine(UUID otherUUID) {      return this.isExpired() || this.getUUID().equals(otherUUID);    }  }}