您好,欢迎访问一九零五行业门户网

redis分布式锁的实现原理实例分析

首先,为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件:
1.互斥性。在任意时刻,只有一个客户端能持有锁。
2.不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。
3.具有容错性。只要大部分的redis节点正常运行,客户端就可以加锁和解锁。
4.解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。
下边是代码实现,首先我们要通过maven引入jedis开源组件,在pom.xml文件加入下面的代码:
<dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-data-redis</artifactid></dependency><dependency> <groupid>redis.clients</groupid> <artifactid>jedis</artifactid> <version>3.1.0</version></dependency>
分布式锁实现代码,distributedlock.java
import redis.clients.jedis.jedis;import redis.clients.jedis.jedispool;import redis.clients.jedis.jedispoolconfig;import redis.clients.jedis.transaction;import redis.clients.jedis.exceptions.jedisexception;import java.util.list;import java.util.uuid;/** * @author swadian * @date 2022/3/4 * @version 1.0 * @describetion redis分布式锁原理 */public class distributedlock { //redis连接池 private static jedispool jedispool; static { jedispoolconfig config = new jedispoolconfig(); // 设置最大连接数 config.setmaxtotal(200); // 设置最大空闲数 config.setmaxidle(8); // 设置最大等待时间 config.setmaxwaitmillis(1000 * 100); // 在borrow一个jedis实例时,是否需要验证,若为true,则所有jedis实例均是可用的 config.settestonborrow(true); jedispool = new jedispool(config, "192.168.3.27", 6379, 3000); } /** * 加锁 * @param lockname 锁的key * @param acquiretimeout 获取锁的超时时间 * @param timeout 锁的超时时间 * @return 锁标识 * redis setnx(set if not exists) 命令在指定的 key 不存在时,为 key 设置指定的值。 * 设置成功,返回 1 。 设置失败,返回 0 。 */ public string lockwithtimeout(string lockname, long acquiretimeout, long timeout) { jedis jedis = null; string retidentifier = null; try { // 获取连接 jedis = jedispool.getresource(); // value值->随机生成一个string string identifier = uuid.randomuuid().tostring(); // key值->即锁名 string lockkey = "lock:" + lockname; // 超时时间->上锁后超过此时间则自动释放锁 毫秒转成->秒 int lockexpire = (int) (timeout / 1000); // 获取锁的超时时间->超过这个时间则放弃获取锁 long end = system.currenttimemillis() + acquiretimeout; while (system.currenttimemillis() < end) { //在获取锁时间内 if (jedis.setnx(lockkey, identifier) == 1) {//关键:设置锁 jedis.expire(lockkey, lockexpire); // 返回value值,用于释放锁时间确认 retidentifier = identifier; return retidentifier; } // ttl以秒为单位返回 key 的剩余过期时间,返回-1代表key没有设置超时时间,为key设置一个超时时间 if (jedis.ttl(lockkey) == -1) { jedis.expire(lockkey, lockexpire); } try { thread.sleep(10); } catch (interruptedexception e) { thread.currentthread().interrupt(); } } } catch (jedisexception e) { e.printstacktrace(); } finally { if (jedis != null) { jedis.close(); } } return retidentifier; } /** * 释放锁 * @param lockname 锁的key * @param identifier 释放锁的标识 * @return */ public boolean releaselock(string lockname, string identifier) { jedis jedis = null; string lockkey = "lock:" + lockname; boolean retflag = false; try { jedis = jedispool.getresource(); while (true) { // 监视lock,准备开始redis事务 jedis.watch(lockkey); // 通过前面返回的value值判断是不是该锁,若是该锁,则删除,释放锁 if (identifier.equals(jedis.get(lockkey))) { transaction transaction = jedis.multi();//开启redis事务 transaction.del(lockkey); list<object> results = transaction.exec();//提交redis事务 if (results == null) {//提交失败 continue;//继续循环 } retflag = true;//提交成功 } jedis.unwatch();//解除监控 break; } } catch (jedisexception e) { e.printstacktrace(); } finally { if (jedis != null) { jedis.close(); } } return retflag; }}
为了验证它,我们创建skillservice.java业务类
import lombok.extern.slf4j.slf4j;@slf4jpublic class skillservice { final distributedlock lock = new distributedlock(); public static final string lock_key = "lock_resource"; int n = 500; /** * 线程业务方法 */ public void seckill() { // 返回锁的value值,供释放锁时候进行判断 string identifier = lock.lockwithtimeout(lock_key, 5000, 1000); log.info("线程:"+thread.currentthread().getname() + "获得了锁"); log.info("剩余数量:{}",--n); lock.releaselock(lock_key, identifier); }}
如果找不到@slf4j日志,在pom.xml文件加入下面的代码:
<!--@slf4j日志依赖组件--> <dependency> <groupid>org.projectlombok</groupid> <artifactid>lombok</artifactid></dependency>
编辑一个测试类testlock.java
/** * @author swadian * @date 2022/3/4 * @version 1.0 */public class testlock { public static void main(string[] args) { skillservice service = new skillservice(); for (int i = 10; i < 60; i++) { //开50个线程 skillthread skillthread = new skillthread(service, "skillthread->" + i); skillthread.start(); } }}class skillthread extends thread { private skillservice skillservice; public skillthread(skillservice skillservice, string skillthreadname) { super(skillthreadname); this.skillservice = skillservice; } @override public void run() { skillservice.seckill(); }}
测试结果显示,加锁后剩余数量全部是顺序串行的,499,498,497...
我们修改skillservice.java业务类,注释掉加锁逻辑
@slf4jpublic class skillservice { final distributedlock lock = new distributedlock(); public static final string lock_key = "lock_resource"; int n = 500; /** * 线程业务方法 */ public void seckill() { // 返回锁的value值,供释放锁时候进行判断 //string identifier = lock.lockwithtimeout(lock_key, 5000, 1000); log.info("线程:"+thread.currentthread().getname() + "获得了锁"); log.info("剩余数量:{}",--n); //lock.releaselock(lock_key, identifier); }}
重新执行测试,注释掉加锁逻辑后,剩余数量全部是乱序的,472,454,452...
以上就是redis分布式锁的实现原理实例分析的详细内容。
其它类似信息

推荐信息