java结合redis实现分布式锁
来源:互联网 发布:租女朋友的软件 编辑:程序博客网 时间:2024/06/07 02:17
今天工作之余,查看一下利用redis来实现分布式锁,因此,在查看别人文章之余,自己也来手动模拟实现Java的lock接口,来自己手动实现一个分布式锁。拥有简单的加锁,解锁,锁中断等操作。
利用redis的分布式锁,主要还是利用redis的setnx命令,查看redis文档,可知次命令在redis缓存中添加数据的时候,如果key存在,则添加数据操作不成功。若不存在,才可以添加成功。从另外一个方面来理解锁(Lock),其实就是一种资源,在某个时刻标记为只能被某个线程使用,若资源已经被使用,则其他线程必须等待当前线程释放资源之后才可以使用。从这个方面理解,也就是,当前线程持有在redis缓存中key的资源,所以其他线程必须等待当前线程释放key的资源,否则只能等待。下面来贴上代码具体分析下:
package com.redislock;import redis.clients.jedis.Jedis;import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;/** * 尝试使用redis实现分布式锁 * Created by hadoop on 2017/3/16. */public class RedisLock implements Lock{
/**jedis客户端**/ private final Jedis jedis;
/**锁定资源的key**/ private final String lockName; /**持有锁的最长时间**/ private final int expireTime = 300; /**获取不到锁的休眠时间**/ private final long sleepTime = 100; /**锁中断状态**/ private boolean interruped = true; /**超时时间**/ private long expireTimeOut = 0; public RedisLock(Jedis jedis, String lockName){ this.jedis = jedis; this.lockName = lockName; } public void lock() { if (jedis == null) throw new NullPointerException("jedis is null"); if (lockName == null) throw new NullPointerException("key is null"); while (true){ if (!interruped) throw new RuntimeException("获取锁状态被中断"); long id = jedis.setnx(lockName, lockName); if (id == 0L){ try { Thread.currentThread().sleep(this.sleepTime); }catch (InterruptedException e){ e.printStackTrace(); } }else{ expireTimeOut = System.currentTimeMillis()/1000 + expireTime; jedis.expireAt(this.lockName, expireTimeOut); break; } } } public void lockInterruptibly() throws InterruptedException { this.interruped = false; } public boolean tryLock() { if (jedis == null) throw new NullPointerException("jedis is null"); if (lockName == null) throw new NullPointerException("lockName is null"); if (!interruped) throw new RuntimeException("线程被中断"); long id = jedis.setnx(lockName, lockName); if (id == 0L) return false; else { // 设置锁过期时间 expireTimeOut = System.currentTimeMillis()/1000 + expireTime; jedis.expireAt(this.lockName, expireTimeOut); return true; } } public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { if (jedis == 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 + calcSeconds(time, unit); while (true){ if (!interruped) throw new InterruptedException("线程被中断"); long id = jedis.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; jedis.expireAt(this.lockName, expireTimeOut); return true; } } } public void unlock() { try { //当前时间小于过期时间,则锁未超时,删除锁定 if (System.currentTimeMillis() / 1000 < expireTimeOut) jedis.del(lockName); }catch (Exception e){ }finally { jedis.close(); } } public Condition newCondition() { throw new UnsupportedOperationException("不支持当前的操作"); } /** * 时间转换成毫秒 * @param time * @param unit * @return */ private long calcSeconds (long time, TimeUnit unit){ if (unit == TimeUnit.DAYS) return time * 24 * 60 * 60 * 1000; else if (unit == TimeUnit.HOURS) return time * 60 * 60 * 1000; else if (unit == TimeUnit.MINUTES) return time * 60 * 1000; else return time * 1000; }}在这里,第一,我是利用实现Lock接口的方式来实现这个锁,在使用锁的时候,一般为一个线程持有一把锁,因此锁可以看成是在线程自己的内部堆栈里面,因此可以不用考虑多线程的支持。重点是在获取锁的接口,获取锁意味着对资源的竞争,因此在本示例中,我尝试循环获取对象的锁,也就是循环向redis里面添加数据,添加成功,则代表加锁成功,添加不成功,则代表加锁失败,休眠一段时间之后,继续获取锁。因此这个lock方法为阻塞的,获取不到锁,就会一直去获取。newCondition方法没有实现,也是基于目前才疏学浅,还不回使用,后面会考虑加上这个newCondition方法。
在这里,说两点,第一点就是redis key的超时,这样可以保证在客户端程序突然down的时候,资源可以在一段时间之后被释放掉,不会产生死锁。还有就是unlock()这个方法,在加锁的时候,会计算锁释放的时间,在释放锁的时候,要判定当前锁是否超时了,若超时,怎不能删除key。否则会破坏线程的安全性。因为不用的redis客户端,虽然不能setnx key值,但是可以del这个key。当然,我这里是一种最简单的处理,在临界条件下也容易出现问题,只是目前可以想到的一种办法。也希望大神们不吝赐教小弟我。
第二点需要说明的是中断状态,基于以前看多线程安全结束的模式,在此处我也是设置标志量interruped 用来标识当前的锁已经被中断,在进行任何操作前,都会先判定这个中断标志,若被中断,则抛出程序异常。 目前还不知道这样做到底会如何,希望路过大神给指导一下意见!!!完全自学,希望不吝赐教。在写的过程中,考虑到一件事情,若中断标志被改变,也就是锁被中断,是否需要调用unlock()方法释放这个锁???
留下诸多疑问,希望路过的朋友留下自己的意见!
- java结合redis实现分布式锁
- Redis分布式锁Java实现
- Redis分布式锁java实现
- java代码实现redis分布式锁代码
- Redis分布式锁实现原理 java版
- Java实现基于Redis的分布式锁
- Java实现基于Redis的分布式锁
- Java实现基于Redis的分布式锁
- Java实现基于Redis的分布式锁
- Java使用Redis实现分布式锁
- java使用redis实现分布式锁
- java实现redis分布式锁实例
- Java实现基于Redis的分布式锁
- Java使用Redis实现分布式锁
- Redis分布式锁的Java实现
- Java使用Redis实现分布式锁
- 单机redis环境java实现分布式锁
- Redis实现分布式锁
- 21天精通java基础之Day15IO流(一)
- java设计模式--代理模式
- IO队列和IO调度
- JAVA基础
- QT之GUI学习笔记(七)----菜单和工具栏
- java结合redis实现分布式锁
- 大数据SQL交互查询 presto/spark/mapreduce 计算引擎对比
- 1010. 一元多项式求导 (25) python篇
- 第一篇博客当作起点
- leetcode 461. Hamming Distance
- JVM很重吗?
- Codeforces Round #404 (Div. 2) A题
- Java和JavaScript中的值传递
- 2015年蓝桥杯省赛B组第3题 【三羊献瑞】