redis分布式锁原理:1、使用Redis的 SETNX 命令可以实现分布式锁SETNX命令:
将 key 的值设为 value,当且仅当 key 不存在。 返回1若给定的 key 已经存在,则 SETNX 不做任何动作。 返回0SETNX 是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();}
来源:https://www.cnblogs.com/liukunjava/p/8205463.html