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); } } }