在集群多机器环境中,为了避免并发导致的冲突问题,就会使用到分布式锁,同一时刻,集群中只有获取到锁的节点能够执行处逻辑,如下:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import java.util.Collections;
@Service("RedisSevice")
public class RedisSevice {
@Autowired
JedisPool jedisPool;
private static final String LOCK_SUCCESS = "OK";
//如果不存在key值就存入redis,如果存在返回失败,过期自动删除
private static final String SET_IF_NOT_EXIST = "NX";
private static final String SET_WITH_EXPIRE_TIME = "PX";
public boolean tryGetDistributedLock(String lockKey, String value, int expireTime) {
Jedis jedis = jedisPool.getResource();
//往redis写入key及value,如果key存在返回写入失败表示获取锁失败,key不存在返回写入成功表示获取锁成功
String result = jedis.set(lockKey, value, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
if (LOCK_SUCCESS.equals(result)) {
return true;
}
return false;
}
private static final Long RELEASE_SUCCESS = 1L;
public boolean releaseDistributedLock(String lockKey, String value) {
Jedis jedis = jedisPool.getResource();
/*当A节点获取锁之后执行过久,导致锁过期自动释放,B节点获取到了锁执行逻辑中,
这时A执行完毕准备释放锁,如果发现value不是自己设置的,说明当前锁已经不是自己持有,则不能释放*/
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(value));
if (RELEASE_SUCCESS.equals(result)) {
return true;
}
return false;
}
}
@Component
public class DistributedLock{
@Autowired
RedisSevice redisSevice;
//redis分布式锁key过期时间,防止执行时间过久或者宕机等异常原因导致锁无法释放
private static final int EXPIRE_TIME = 30000;
public void main(String[] args) {
String lockKey = "key值";
String lockValue = "value值,可用集群机器IP表明锁的持有者";
boolean lockable = redisSevice.tryGetDistributedLock(lockKey, lockValue, EXPIRE_TIME);
if (lockable) {
try {
//获取到锁,执行业务逻辑
} finally {
//执行结束释放锁
redisSevice.releaseDistributedLock(lockKey, value);
}
}
}
}
上述情况主要适用于单机redis,如果是集群,master数据未同步到salve节点时宕机,会导致锁丢失的问题,推荐使用Redisson分布式锁。