一 前言
1.在多进程服务的情况下,无法通过共享锁或者synchronized关键字来实现对共享资源的访问,所以需要引入分布式锁,解决多个服务同步访问共享资源的问题。
二 分布式锁要解决两个问题:
(1) 线程在获取锁时,具有对资源的独占权利,并且设置超时时间,以免自身线程崩溃或占用过长,导致其他线程无法获取资源的问题
(2) 线程需要在获取锁后,对资源进行独占,进行业务处理后,未达到超时时间时,需要对锁资源及时释放。但是要注意,如果占锁线程执行时间过长,导致锁超时时间用完后,需要判断是否仍是自己获取的锁,以免释放掉别人的锁,
可以通过设置 redis key的value(UUID)来为唯一标识,占锁线程判断是否为其设置的value,进而决定是否为自身锁资源。
三 代码实现
package com.wacai.stanlee.capm.util;
import redis.clients.jedis.JedisCluster;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
public class RedisLockUtil {
private static final String LOCK_SUCCESS = "OK";
private static final Long RELEASE_SUCCESS = 1L;
/**
* 使用lua对获取当前锁,并设置过期时间
* @param jedisCluster
* @param lock redis key
* @param value 可设置java的UUID作为redis key的值
* @param expireTime 单位为秒,默认20s
* @return
*/
public static boolean tryGetDistributedLock(JedisCluster jedisCluster, String lock, String value, int expireTime) {
//串行、原子操作,保证设置lock,设置过期时间
List<String> paras = new ArrayList<>();
String luaOfTryLock = "return redis.call('set',KEYS[1],ARGV[1],'NX','EX',ARGV[2])";
paras.add(value);
if(0 != expireTime){
paras.add(String.valueOf(expireTime));
}else{
paras.add("20");
}
Object result = jedisCluster.eval(luaOfTryLock, Collections.singletonList(lock), paras);
return null != result && LOCK_SUCCESS.equalsIgnoreCase(result.toString()) ;
}
/**
* 在删除锁时,防止A线程把B线程的锁释放
* 如果redis的value与所属线程加锁时的值一样,则进行锁资源释放,否则不进行释放
* @param jedisCluster
* @param lockKey
* @param currentValues
* @return
*/
public static boolean realseLock(JedisCluster jedisCluster, String lockKey, String currentValues) {
String luaOfReleaseLock = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return -1 end";
Object result = jedisCluster.eval(luaOfReleaseLock, Collections.singletonList(lockKey), Collections.singletonList(currentValues));
return null != result && RELEASE_SUCCESS == Integer.parseInt(result.toString());
}
}