public class RedisLock {
private static Logger logger = LoggerFactory.getLogger(RedisLock.class);
private static final String LOCK_MSG = "OK";
private static final Long UNLOCK_MSG = 1L;
private static final String SET_IF_NOT_EXIST = "NX";
private static final String SET_WITH_EXPIRE_TIME = "PX";
private String lockPrefix;
private int sleepTime;
private JedisTemplate jedisTemplate;
/**
* time millisecond
*/
private static final int TIME = 1000;
/**
* lua script
*/
private String script;
private RedisLock(Builder builder) {
this.jedisTemplate = builder.jedisTemplate;
this.lockPrefix = builder.lockPrefix;
this.sleepTime = builder.sleepTime;
buildScript();
}
/**
* Non-blocking lock
*
* @param key lock business type
* @param request value
* @return true lock success
* false lock fail
*/
public boolean tryLock(final String key, final String request) {
String result = set(lockPrefix + key, request, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, 10 * TIME);
if (LOCK_MSG.equals(result)) {
return true;
} else {
return false;
}
}
/**
* jedis 方法
*
* @param key
* @param value
* @param nxxx
* @param expx
* @param time
* @return
*/
private String set(final String key, final String value, final String nxxx, final String expx,
final int time) {
return jedisTemplate.execute(new Function<Jedis, String>() {
@Override
public String apply(Jedis jedis) {
return jedis.set(key, value, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, time * TIME);
}
});
}
/**
* blocking lock
*
* @param key
* @param request
*/
public void lock(String key, String request) throws InterruptedException {
//get connection
String result;
for (; ; ) {
result = set(lockPrefix + key, request, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, 10 * TIME);
if (LOCK_MSG.equals(result)) {
break;
}
Thread.sleep(sleepTime);
}
}
/**
* blocking lock,custom time
*
* @param key
* @param request
* @param blockTime custom time
* @return
* @throws InterruptedException
*/
public boolean lock(String key, String request, int blockTime) throws InterruptedException {
String result;
while (blockTime >= 0) {
result = set(lockPrefix + key, request, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, 10 * TIME);
if (LOCK_MSG.equals(result)) {
return true;
}
blockTime -= sleepTime;
Thread.sleep(sleepTime);
}
return false;
}
/**
* Non-blocking lock
*
* @param key lock business type
* @param request value
* @param expireTime custom expireTime
* @return true lock success
* false lock fail
*/
public boolean tryLock(String key, String request, int expireTime) {
//get connection
String result;
result = set(lockPrefix + key, request, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
if (LOCK_MSG.equals(result)) {
return true;
} else {
return false;
}
}
/**
* unlock
*
* @param key
* @param request request must be the same as lock request
* @return
*/
public boolean unlock(String key, String request) {
//lua script
Object result = null;
result = eval(script, Collections.singletonList(lockPrefix + key), Collections.singletonList(request));
if (UNLOCK_MSG.equals(result)) {
return true;
} else {
return false;
}
}
/**
* redis eval 方法
*
* @param script
* @param keys
* @param args
* @return
*/
private Object eval(final String script, final List<String> keys, final List<String> args) {
return jedisTemplate.execute(new Function<Jedis, Object>() {
@Override
public Object apply(Jedis jedis) {
return jedis.eval(script, keys, args);
}
});
}
/**
* read lua script
*/
private void buildScript() {
try {
script = FileUtil.readString(new ClassPathResource("lock.lua").getURL(), CharsetUtil.UTF_8);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static class Builder {
private static final String DEFAULT_LOCK_PREFIX = "lock_";
/**
* default sleep time
*/
private static final int DEFAULT_SLEEP_TIME = 100;
private JedisTemplate jedisTemplate = null;
private String lockPrefix = DEFAULT_LOCK_PREFIX;
private int sleepTime = DEFAULT_SLEEP_TIME;
public Builder(JedisTemplate jedisTemplate) {
this.jedisTemplate = jedisTemplate;
}
public Builder lockPrefix(String lockPrefix) {
this.lockPrefix = lockPrefix;
return this;
}
public Builder sleepTime(int sleepTime) {
this.sleepTime = sleepTime;
return this;
}
public RedisLock build() {
return new RedisLock(this);
}
}
}
lock.lua
if redis.call('get', KEYS[1]) == ARGV[1] then
return redis.call('del', KEYS[1])
else
return 0
end