大家都知道我们的jdk锁和syncronized同步锁都是基于单jvm的,但是在分布式系统中由于应用部署在多台服务器
我们要实现跟单jvm一样的原子化效果必须依赖于应用服务器以外的节点,所以目前用的最多的便是利用redis和zookeeper实现的分布式锁,今天我们主要看下利用redis如何操作。
上代码:
import redis.clients.jedis.Jedis;
import java.util.Collections;
/**
* 利用jedis实现的redis分布式锁
*
* NX:表示set方法启用NX模式(SET_IF_NOT_EXIST)即如果存在key不做操作,否则执行set操作
* PX:表示给set的key值设置一个过期时间(SET_WITH_EXPIRE_TIME)
* 这里要注意requestId这个参数,很重要,在加锁和解锁的过程都需要同一个requestId,即解铃还须系铃人的道 理。
* 也可以将这个值放进ThreadLocal里边进行传递。
*/
public class RedisTool {
private static final String LOCK_SUCCESS = "OK";
private static final String SET_IF_NOT_EXIST = "NX";
private static final String SET_WITH_EXPIRE_TIME = "PX";
private static final Long RELEASE_SUCCESS = 1L;
/**
* 尝试获取分布式锁
* @param jedis Redis客户端
* @param lockKey 锁
* @param requestId 请求标识
* @param expireTime 超期时间
* @return 是否获取成功
*/
public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {
String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
if (LOCK_SUCCESS.equals(result)) {
return true;
}
return false;
}
/**
* 释放分布式锁
* @param jedis Redis客户端
* @param lockKey 锁
* @param requestId 请求标识
* @return 是否释放成功
*/
public static boolean releaseDistributedLock(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脚本封装了一个原子化操作:获取锁的值key的值进行比对,如果一致删除这个key否则返回0,结束。