前言
最近在工作中应用到了分布式锁,所以在本文中记录一下如何在Java中应用Redis实现分布式锁
四个个性
在应用分布式锁的时候,咱们须要满足以下四个条件:
互斥性
在同一时间只能有一个客户端持有锁。容错性
客户端能够实现加锁和解锁。可靠性
即便在客户端解体后,无奈被动开释锁的状况下, 也能够保障后续客户端持有该锁。一致性
加锁和解锁需是同一客户端,必须防止以后客户端的锁被其余客户端解锁。
具体实现
加锁
间接上代码,加锁其实并不简单。
private ThreadLocal<String> threadLocal = new ThreadLocal<>();private static final String LOCK_SUCCESS = "OK";private static final String LOCK_KEY = "redisLockTest"; public void test() { Jedis jedis = getJedis(); String uuid = UUID.randomUUID().toString(); threadLocal.set(uuid); boolean result = tryLock(jedis,LOCK_KEY,uuid,10); if (result) { //......具体业务实现 } } /** * 获取分布式锁 * * @param jedis Redis客户端 * @param lockKey 锁 * @param requestId 申请标识 * @param expireTime 过期工夫 * @return 是否获取胜利 * */ public static boolean tryLock(Jedis jedis, String lockKey, String requestId, int expireTime) { String result = jedis.set(lockKey, requestId, "NX", "PX", expireTime); if (LOCK_SUCCESS.equals(result)) { return true; } return false; }
具体外围代码其实就是这一行:
String result = jedis.set(lockKey, requestId, "NX", "PX", expireTime);
- 第一个参数为Key,锁的名称,并且是全局惟一。
- 第二个参数为value,这里是应用的一串随机字符,存入以后线程本地外面。value具体的作用是要在解锁的时候,判断加锁解锁是否为一个客户端。
- 第三个参数为“NX” ,意思是SET IF NOT EXIST,即当key不存在时,咱们进行set操作;若key曾经存在,则不做任何操作。
- 第四个参数为"PX",这个参数是过期工夫单位,意思是咱们要给这个key加一个过期工夫,如果客户端在操作的时候忽然宕机,Redis在过期后进行主动删除,防止长时间持有锁造成死锁。
- 第五个参数为expireTime,具体的过期工夫。
解锁
private ThreadLocal<String> threadLocal = new ThreadLocal<>(); private static final String LOCK_SUCCESS = "OK"; private static final String LOCK_KEY = "redisLockTest"; private static final Long RELEASE_SUCCESS = 1L; public void test() { try { Jedis jedis = getJedis(); boolean result = releaseLock(jedis, LOCK_KEY, threadLocal.get()); if (result) { //......具体业务实现 } }finally { threadLocal.remove(); }}/** * 开释分布式锁 * @param jedis Redis客户端 * @param lockKey 锁 * @param requestId 申请标识 * @return 是否开释胜利 */public static boolean releaseLock(Jedis jedis, String lockKey, String requestId) { String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId)); if (RELEASE_SUCCESS.equals(result)) { return true; } return false;}
对于解锁来说,咱们须要应用lua脚本来实现解锁,因为咱们须要在解锁时判断以后锁是否是以后客户端所持有,所以咱们要先进行get判断key和value是否统一,如果统一咱们进行删除。这个中央咱们不能应用平时的redis命令来进行操作,因为咱们要执行两个命令,须要保障原子性,须要应用lua脚本来保障原子性。
简略来说,就是在eval命令执行Lua代码的时候,Lua代码将被当成一个命令去执行,并且直到eval命令执行实现,Redis才会执行其余命令。
ps:如果不明确lua脚本的相干常识请谷歌学习。
总结
其实实现一个单机Redis版的分布式锁并不是很难,只须要保障其四个个性即可。
如果文章中有谬误中央,还请大家指出,共同进步。