分布式锁Redis实现

上篇讲了分布式锁的数据库实现,这篇我们继续来讲分布式锁的redis实现。
那么如何通过redis来实现一个分布式锁呢? 一般最容易想到的命令,就是setNx
那在使用redis setNx命令时,还需要关注哪些点呢?

  1. redis分布式锁常用命令,SETNX(key, val)
    当且仅当key不存在时,设置成功,返回“1”,否者什么都不做,返回“0”
    我们可以利用该命令的特性进行加锁操作。
    假如同时有两个线程要竞争资源,其中一个线程先获取到资源,调用setnx命令,将设置成功,返回“1”,表示加锁成功。另一个线程也调用setnx命令,返回“0”,表示该key已经设置过了,加锁失败,需要重新等待。
  2. expire key timeout
    为了防止,加锁过程中出现异常,锁一直不释放,需要给加锁的key,设置一个超时时间,超时时间的设置,是所有的分布式锁实现都需要考虑的。
  3. delete key
    删除某个key,释放对应的锁。
【分布式锁Redis实现】下面是简单的源码实现:
/** * 分布式锁的简单实现代码 */ public class DistributedLock {private final JedisPool jedisPool; public DistributedLock(JedisPool jedisPool) { this.jedisPool = jedisPool; }/** * 加锁 * @param lockName锁的key * @param acquireTimeout 获取超时时间 * @param timeout锁的超时时间 * @return 锁标识 */ public String lockWithTimeout(String lockName, long acquireTimeout, long timeout) { Jedis conn = null; String retIdentifier = null; try { // 获取连接 conn = jedisPool.getResource(); // 随机生成一个value String identifier = UUID.randomUUID().toString(); // 锁名,即key值 String lockKey = "lock:" + lockName; // 超时时间,上锁后超过此时间则自动释放锁 int lockExpire = (int) (timeout / 1000); // 获取锁的超时时间,超过这个时间则放弃获取锁 long end = System.currentTimeMillis() + acquireTimeout; while (System.currentTimeMillis() < end) { if (conn.setnx(lockKey, identifier) == 1) { conn.expire(lockKey, lockExpire); // 返回value值,用于释放锁时间确认 retIdentifier = identifier; return retIdentifier; } // 返回-1代表key没有设置超时时间,为key设置一个超时时间 if (conn.ttl(lockKey) == -1) { conn.expire(lockKey, lockExpire); }try { Thread.sleep(10); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } catch (JedisException e) { e.printStackTrace(); } finally { if (conn != null) { conn.close(); } } return retIdentifier; }/** * 释放锁 * @param lockName锁的key * @param identifier 释放锁的标识 * @return */ public boolean releaseLock(String lockName, String identifier) { Jedis conn = null; String lockKey = "lock:" + lockName; boolean retFlag = false; try { conn = jedisPool.getResource(); while (true) { // 监视lock,准备开始事务 conn.watch(lockKey); // 通过前面返回的value值判断是不是该锁,若是该锁,则删除,释放锁 if (identifier.equals(conn.get(lockKey))) { Transaction transaction = conn.multi(); transaction.del(lockKey); List results = transaction.exec(); if (results == null) { continue; } retFlag = true; } conn.unwatch(); break; } } catch (JedisException e) { e.printStackTrace(); } finally { if (conn != null) { conn.close(); } } return retFlag; } }

    推荐阅读