Redis——实现悲观锁
Posted 水田如雅
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis——实现悲观锁相关的知识,希望对你有一定的参考价值。
抽象类:
/**
* \\* Created: liuhuichao
* \\* Date: 2018/6/5
* \\* Time: 上午10:35
* \\* Description: redis 锁抽象类
* \\
*/
public abstract class AbstractLock implements Lock
protected volatile boolean locked;
private Thread exclusiveOwnerThread;
@Override
public void lock()
try
lock(false, 0, null, false);
catch (InterruptedException e)
@Override
public void lockInterruptibly() throws InterruptedException
lock(false, 0, null, true);
public boolean tryLockInterruptibly(long time, TimeUnit unit) throws InterruptedException
return lock(true, time, unit, true);
@Override
public boolean tryLock(long time, TimeUnit unit)
try
System.out.println("try--lock");
return lock(true, time, unit, false);
catch (InterruptedException e)
e.printStackTrace();
System.out.println("" + e);
return false;
@Override
public void unlock()
// 检查当前线程是否持有锁
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException("current thread does not hold the lock");
unlock0();
setExclusiveOwnerThread(null);
protected void setExclusiveOwnerThread(Thread thread)
exclusiveOwnerThread = thread;
protected final Thread getExclusiveOwnerThread()
return exclusiveOwnerThread;
protected abstract void unlock0();
/**
* 阻塞式获取锁的实现
* @param useTimeout
* @param time
* @param unit
* @param interrupt 是否响应中断
* @return
* @throws InterruptedException
*/
protected abstract boolean lock(boolean useTimeout, long time, TimeUnit unit, boolean interrupt)
throws InterruptedException;
实现类:
/**
* \\* Created: liuhuichao
* \\* Date: 2018/6/5
* \\* Time: 下午4:09
* \\* Description:基于Redis的SETNX操作实现的分布式锁基于Redis的SETNX操作实现的分布式锁
* 获取锁时最好用lock(long time, TimeUnit unit), 以免网路问题而导致线程一直阻塞
*
* SETNX命令(SET if Not eXists)
语法:
SETNX key value
功能:
当且仅当 key 不存在,将 key 的值设为 value ,并返回1;若给定的 key 已经存在,则 SETNX 不做任何动作,并返回0。
GETSET命令(这是一个原子命令!)
语法:
GETSET key value
功能:
将给定 key 的值设为 value ,并返回 key 的旧值 (old value),当 key 存在但不是字符串类型时,返回一个错误,当key不存在时,返回nil。
* \\
*/
public class RedisBasedOnSetNxLock extends AbstractLock
private Jedis jedis;
// 锁的名字
protected String lockKey;
// 锁的有效时长(毫秒)
protected long lockExpires;
public RedisBasedOnSetNxLock(Jedis jedis, String lockKey, long lockExpires)
this.jedis = jedis;
this.lockKey = lockKey;
this.lockExpires = lockExpires;
@Override
protected void unlock0()
@Override
protected boolean lock(boolean useTimeout, long time, TimeUnit unit, boolean interrupt) throws InterruptedException
if(interrupt)
checkInterruption();
long start = System.currentTimeMillis();
long timeout = unit.toMillis(time);
while (useTimeout ? isTimeout(start, timeout) : true)
if (interrupt)
checkInterruption();
long lockExpireTime = System.currentTimeMillis() + lockExpires + 1;// 锁超时时间
String stringOfLockExpireTime = String.valueOf(lockExpireTime);
if (jedis.setnx(lockKey, stringOfLockExpireTime) == 1) // 获取到锁
//成功获取到锁, 设置相关标识
locked = true;
setExclusiveOwnerThread(Thread.currentThread());
return true;
String value = jedis.get(lockKey);
if (value != null && isTimeExpired(value)) // lock is expired
// 假设多个线程(非单jvm)同时走到这里
String oldValue = jedis.getSet(lockKey, stringOfLockExpireTime); //原子操作
/**
* 但是走到这里时每个线程拿到的oldValue肯定不可能一样(因为getset是原子性的)
* 假如拿到的oldValue依然是expired的,那么就说明拿到锁了
*/
if (oldValue != null && isTimeExpired(oldValue))
//成功获取到锁, 设置相关标识
locked = true;
setExclusiveOwnerThread(Thread.currentThread());
return true;
else
// TODO lock is not expired, enter next loop retrying
return false;
public boolean tryLock()
long lockExpireTime = System.currentTimeMillis() + lockExpires + 1;// 锁超时时间
String stringOfLockExpireTime = String.valueOf(lockExpireTime);
if (jedis.setnx(lockKey, stringOfLockExpireTime) == 1) // 获取到锁
// 成功获取到锁, 设置相关标识
locked = true;
setExclusiveOwnerThread(Thread.currentThread());
return true;
String value = jedis.get(lockKey);
if (value != null && isTimeExpired(value)) // lock is expired
// 假设多个线程(非单jvm)同时走到这里
String oldValue = jedis.getSet(lockKey, stringOfLockExpireTime); //原子操作
// 但是走到这里时每个线程拿到的oldValue肯定不可能一样(因为getset是原子性的)
// 假如拿到的oldValue依然是expired的,那么就说明拿到锁了
if (oldValue != null && isTimeExpired(oldValue))
//成功获取到锁, 设置相关标识
locked = true;
setExclusiveOwnerThread(Thread.currentThread());
return true;
else
// TODO lock is not expired, enter next loop retrying
return false;
private void checkInterruption() throws InterruptedException
if (Thread.currentThread().isInterrupted())
throw new InterruptedException();
private boolean isTimeExpired(String value)
return Long.parseLong(value) < System.currentTimeMillis();
/**
* judge whether timeout or not
* @param start
* @param timeout
* @return if timeout return true; else false
*/
private boolean isTimeout(long start, long timeout)
return start + timeout > System.currentTimeMillis();
private void doUnlock()
jedis.del(lockKey);
@Override
public Condition newCondition()
return null;
锁续命
加锁之后,开启一个线程,对当前加锁的操作,进行不断的更新过期时间的操作,直到锁被释放;
我们redis的客户端redisson提供了锁续命以及加锁解锁的安全操作。
RLock lock = client.getNativeClient().getLock("key");
try
lock.lock();
//这里写加锁的逻辑。。。
finally
lock.unlock();
以上是关于Redis——实现悲观锁的主要内容,如果未能解决你的问题,请参考以下文章