您的位置:首页 > 数据库 > Redis

简单实现了一个基于redis的分布式锁,存在bug...

2017-06-06 00:00 751 查看

直接上代码

中间存在一个bug,可能导致死锁.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import redis.clients.jedis.Jedis;

import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

/**
* Created by wenshiliang on 2017/6/5.
* 不太可靠的基于redis的分布式锁
* setnx一个跟随jvm的uuid到redis,成功,认为占用锁成功,设置thread.
*
* 释放锁:判断是不是当前thread占用的,判断是不是当前jvm占用的,是,del redis key.
*
*
* bug: 当异常退出jvm时,可能导致lock未释放.一般情况下设置了redis value的有效时长.如果在setnx后,expire前异常中断了jvm.可能会导致死锁.
*/
public class RedisLock {

public static final int LOCK_DEATH_TIME = 300;//lock锁住时长,防止死锁,单位seconds

private static final Logger LOGGER = LoggerFactory.getLogger(RedisLock.class);

private final static String UID = UUID.randomUUID().toString();

private final static ConcurrentHashMap<RedisLock, Object> LOCK_MAP = new ConcurrentHashMap<>();

private RedisTemplate<String, String> redisTemplate;

private Thread exclusiveOwnerThread;

private String key;

static {
//钩子,退出jvm时,移除lock
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
System.out.println("redis lock shutdown hook");
LOCK_MAP.entrySet().forEach(entry -> {
entry.getKey().forceRelease();
});
}));
}

public RedisLock(RedisTemplate<String, String> redisTemplate, String key) {
this.redisTemplate = redisTemplate;
this.key = key;
}

public void acquire() {
int i=1;
while (!acquire(1000)){
if(i++>300){
LOGGER.warn("lock acquire wait");
}
};
}

/**
* 获得锁
* @param time 毫秒
* @return
*/
public boolean acquire(final long time) {

if (Objects.equals(Thread.currentThread(), exclusiveOwnerThread)) {
String result = redisTemplate.opsForValue().get(key);
if (UID.equals(result)) {
return true;
}
setExclusiveOwnerThread(null);
}
boolean flag = false;
long start = System.currentTimeMillis();
while (!flag && start <= System.currentTimeMillis() + time) {

flag = redisTemplate.execute(new RedisCallback<Boolean>() {
@Override
public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
Jedis jedis = (Jedis) connection.getNativeConnection();
if (jedis.setnx(key, UID) == 1L) {
jedis.expire(key, LOCK_DEATH_TIME);//300秒过期,防止死锁.如果在这步前jvm挂了,会导致一直死锁.
LOCK_MAP.put(RedisLock.this,1);
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
});
}

return flag;
}

public void release() {
if (Objects.equals(Thread.currentThread(), exclusiveOwnerThread)) {
String result = redisTemplate.opsForValue().get(key);
if (UID.equals(result)) {
LOCK_MAP.remove(this);
setExclusiveOwnerThread(null);
redisTemplate.delete(key);
}
}
}

protected void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}

private void forceRelease() {
String result = redisTemplate.opsForValue().get(key);
if (UID.equals(result)) {
LOCK_MAP.remove(this);
setExclusiveOwnerThread(null);
redisTemplate.delete(key);
LOGGER.info("force release lock: " + key);
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  redis lock jedis lock lock