介绍
Redis 2.8 版本中加入了 set 指令的扩展参数,使得 setnx 和 expire 指令可以一起执行,彻底解决了分布式锁的乱象
set lock:codehole true ex 5 nx
OK
... do something critical ...
> del lock:codehole
上面这个指令就是 setnx 和 expire 组合在一起的原子指令,它就是分布式锁的奥义所在
定义接口
public interface DistributedLock {
/**
* 获取锁
*
* @param jedis
* @param lockkey
* @param requestId
* @param exprietime
*/
void lock(Jedis jedis, String lockkey, String requestId, int exprietime);
/**
* 解锁
*/
void unlock(Jedis jedis, String lockkey, String requestId);
}
工具类
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;
private static String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {
String lock = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
if (LOCK_SUCCESS.equalsIgnoreCase(lock)) {
return true;
}
return false;
}
public static boolean realseLock(Jedis jedis, String lockKey, String requestId) {
Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
if (RELEASE_SUCCESS.equals(result)) {
return true;
}
return false;
}
}
实现
public class RedisLock implements DistributedLock {
@Override
public void lock(Jedis jedis, String lockkey, String requestId, int exprietime) {
while (true) {
boolean lock = RedisTool.tryGetDistributedLock(jedis, lockkey, requestId, exprietime);
if (lock) {
break;
}
}
}
@Override
public void unlock(Jedis jedis, String lockkey, String requestId) {
while (true) {
boolean release = RedisTool.realseLock(jedis, lockkey, requestId);
if (release) {
break;
}
}
}
}
测试
package com.ghgcn.redis.lock2;
import java.util.Random;
import java.util.UUID;
import redis.clients.jedis.JedisPool;
public class LockThread extends Thread {
DistributedLock lock = new RedisLock();
String lockkey = "lockkey";
// String requestId = "lockkey:requestId";
String requestId = UUID.randomUUID()
.toString();
JedisPool poolConfig = new JedisPool();
Random random = new Random();
@Override
public void run() {
lock.lock(poolConfig.getResource(), lockkey, requestId, 3000);
System.out.println(Thread.currentThread()
.getName() + "... get locked");
try {
Thread.sleep(random.nextInt(3000));
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.unlock(poolConfig.getResource(), lockkey, requestId);
System.out.println(Thread.currentThread()
.getName() + "... realse lock");
}
}
public class LockThreadTest {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
new LockThread().start();
}
}
}
结果
Thread-0… get locked
Thread-0… realse lock
Thread-4… get locked
Thread-4… realse lock
Thread-5… get locked
Thread-5… realse lock
Thread-2… get locked
Thread-2… realse lock
Thread-3… get locked
Thread-3… realse lock
以上为简单实现