锁Redis Java实现锁机制及过期处理(redisjava过期)

Redis作为一种高性能的key-value数据库,被广泛应用在分布式系统中,Redis的原子性及高性能特点容易被分布式系统用于实现分布式锁,以防止共享数据的修改。

Redis实现锁机制可使用setNx+expire命令来实现,setNx命令可以在当前实例判断key是否还未被获得,如果key不存在,表示当前实例获得了该key的控制权,此时就可以使用expire命令设置该key的有效期.这样客户端在获得key的控制权之后,就可以让key在一定时间内保持有效,从而保证数据没有多个客户端同时修改的风险。

在Java中,使用Redis实现分布式锁,可以利用RedisTemplate来操作Redis,以JUC为基础,可通过实现一个locker接口来实现一个基于Redis实现分布式锁的locker类,如下所示:

public interface Locker {

boolean lock(String key);

boolean unlock(String key);
}

public class RedisLocker implements Locker {
private RedisTemplate redisTemplate;
private static final int DEFAULT_ACQUIRY_RESOLUTION_MILLIS = 100;

/**
* 锁超时时间,防止线程在入锁以后,无限的执行等待
*/
private int expireMsecs = 60 * 1000;
/**
* 锁等待时间,防止线程饥饿
*/
private int timeoutMsecs = 10 * 1000;
private boolean locked = false;

public RedisLocker(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
@Override
public synchronized boolean lock(String key) {
int timeout = timeoutMsecs;
while (timeout >= 0) {
long expires = System.currentTimeMillis() + expireMsecs + 1;
String expiresStr = String.valueOf(expires); //锁到期时间
if (this.redisTemplate.opsForValue().setIfAbsent(key, expiresStr)) {
locked = true;
return true;
}
String currentValueStr = (String) redisTemplate.opsForValue().get(key); //redis里的时间
if (currentValueStr != null && Long.parseLong(currentValueStr)
//判断是否为空,不为空的情况下,如果被其他线程设置了值,则第二个条件判断是过不去的
String oldValueStr = this.redisTemplate.opsForValue().getAndSet(key, expiresStr);
//获取上一个锁到期时间,并设置现在的锁到期时间,
//只有一个线程才能获取上一个线上的设置时间,因为jedis.getSet是同步的
if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {
//防止误删(覆盖,因为key是相同的)了他人的锁——这里达不到效果,这里值会被覆盖,但是因为什么相差了很少的时间,所以可以接受
locked = true;
return true;
}
}
timeout -= DEFAULT_ACQUIRY_RESOLUTION_MILLIS;
/*
延迟100毫秒, 这里使用随机时间可能会好一点,可以防止饥饿进程的出现,即,当同时到达多个进程,
只会有一个进程获得锁,其他的都用同样的频率进行尝试,后面有来了一些进行,也以同样的频率申请锁,
这将可能导致前面来的锁得不到满足.使用随机的等待时间可以一定程度上保证公平性
*/
try {
Thread.sleep(DEFAULT_ACQUIRY_RESOLUTION_MILLIS);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return false;
}
@Override
public synchronized boolean unlock(String key) {
if (locked) {
redisTemplate.delete(key);
locked = false;
return true;
}
return false;
}
}

以上实现了基于Redis实现的分布式锁,不过由于其是基于时间的实现,也就意味着,如果锁的拥有者在有效期到期之前没有完成持有锁的任务,或者由于宕机等原因Decression时锁未能被删除,则锁会在有效期内永久失效,这是实际开发应用中需要注意处理的


数据运维技术 » 锁Redis Java实现锁机制及过期处理(redisjava过期)