redis分布式锁

redis分布式锁原理:
1、使用Redis的 SETNX 命令可以实现分布式锁
SETNX命令:
将 key 的值设为 value,当且仅当 key 不存在。 返回1
若给定的 key 已经存在,则 SETNX 不做任何动作。 返回0
SETNX 是SET if Not eXists的简写。

public class RedisLock implements Lock{

/**锁定资源的key**/
private final String lockName;
/**持有锁的最长时间**/
private final int expireTime = 300;
/**获取不到锁的休眠时间**/
private final long sleepTime = 100;
/**锁中断状态**/
private boolean interruped = true;
/**超时时间**/
private long expireTimeOut = 0;
RedisClientTemplate redisClient = SpringContextHolder
.getBean("redisClientTemplate");

public RedisLock(String lockName){
this.lockName = lockName;
}

@Override
public void lock() {
// TODO Auto-generated method stub
if (lockName == null)
throw new NullPointerException("key is null");
while (true){
if (!interruped)
throw new RuntimeException("获取锁状态被中断");
long id = redisClient.setnx(lockName, lockName);
if (id == 0L){
try {
Thread.currentThread().sleep(this.sleepTime);
}catch (InterruptedException e){
e.printStackTrace();
}
}else{
expireTimeOut = System.currentTimeMillis()/1000 + expireTime;
redisClient.expireAt(this.lockName, expireTimeOut);
break;
}
}
}

@Override
public void lockInterruptibly() throws InterruptedException {
// TODO Auto-generated method stub
this.interruped = false;
}

@Override
public Condition newCondition() {
// TODO Auto-generated method stub
return null;
}

@Override
public boolean tryLock() {
// TODO Auto-generated method stub
if (redisClient == null)
throw new NullPointerException("jedis is null");
if (lockName == null)
throw new NullPointerException("lockName is null");
if (!interruped)
throw new RuntimeException("线程被中断");
long id = redisClient.setnx(lockName, lockName);
if (id == 0L)
return false;
else {
// 设置锁过期时间
expireTimeOut = System.currentTimeMillis()/1000 + expireTime;
redisClient.expireAt(this.lockName, expireTimeOut);
return true;
}
}

@Override
public boolean tryLock(long time, TimeUnit unit)
throws InterruptedException {
if (redisClient == null)
throw new NullPointerException("jedis is null");
if (lockName == null)
throw new NullPointerException("lockName is null");
if (time == 0)
return false;
long now = System.currentTimeMillis();
long timeOutAt = now + DateUtil.calcSeconds(time, unit);
while (true){
if (!interruped)
throw new InterruptedException("线程被中断");
long id = redisClient.setnx(this.lockName, this.lockName);
// id = 0 表示加锁失败
if(id == 0){
// 获取锁超时
if(System.currentTimeMillis() > timeOutAt){
return false;
}
try {
// 休眠一段时间,继续获取锁
Thread.currentThread().sleep(this.sleepTime);
}catch (InterruptedException e){
e.printStackTrace();
}
}else {
//获取锁成功,设置锁过期时间
expireTimeOut = System.currentTimeMillis()/1000 + expireTime;
redisClient.expireAt(this.lockName, expireTimeOut);
return true;
}
}
}
补充:
为了让分布式锁的算法更稳键些,持有锁的客户端在解锁之前应该再检查一次自己的锁是否已经超时,再去做DEL操作,
因为可能客户端因为某个耗时的操作而挂起,操作完的时候锁因为超时已经被别人获得,这时就不必解锁了。
 
注释掉的部分是为了确保拿到锁的线程释放锁
@Override
public void unlock() {
// TODO Auto-generated method stub
// try {
if (System.currentTimeMillis() / 1000 < expireTimeOut) {
redisClient.del(lockName);
// }catch (Exception e){

// }finally {
// redisClient.del(lockName);
//}
}


}
以下是处理并发应用的代码块:
RedisLock lock = new RedisLock("key");
try {
if(lock.tryLock(3, TimeUnit.SECONDS)){
//共享资源的操作
    lock.unlock();
    }
}
catch (Exception e){
  e.printStackTrace();
}finally {
// lock.unlock();
}
posted @ 2018-01-05 16:41  人情世故zzz  阅读(228)  评论(0编辑  收藏  举报