分布式锁可以基于很多种方式实现,比如zookeeper、redis...。不管哪种方式,他的基本原理是不变的:用一个状态值表示锁,对锁的占用和释放通过状态值来标识。
一、为什么Redis可以方便地实现分布式锁
1、Redis为单进程单线程模式,采用队列模式将并发访问变成串行访问,且多客户端对Redis的连接并不存在竞争关系。
2、Redis的SETNX命令可以方便的实现分布式锁。
setNX(SET if Not eXists)
语法:SETNX key value
返回值:设置成功,返回 1 ;设置失败,返回 0 。
当且仅当 key 不存在时将 key 的值设为 value,并返回1;若给定的 key 已经存在,则 SETNX 不做任何动作,并返回0。
综上所述,可以通过setnx的返回值来判断是否获取到锁,并且不用担心并发访问的问题,因为Redis是单线程的,所以如果返回1则获取到锁,返回0则没获取到。当业务操作执行完后,一定要释放锁,释放锁的逻辑很简单,就是把之前设置的key删除掉即可,这样下次又可以通过setnx该key获取到锁了。
二、分布式锁实现
我们已经知道可以通过Redis自带的函数setNX来实现分布式锁,具体实现步骤如下。
我在一台CentOS7的linux虚拟机中安装了Redis服务,ip地址为:192.168.246.130,服务端口为:6379。
下面是java通过redis实现分布式锁的例子:
import redis.clients.jedis.Jedis; public class RedisLock { //锁的key private static final String key = "DistributedRedisLock"; private static Integer count = 0; public static void main(String[] args) { for(int i=0;i<1000;i++){ new Thread(new Runnable() { @Override public void run() { //获取Redis连接 Jedis jedis = new Jedis("192.168.246.130", 6379); try{ while(true){ //获取锁 if(jedis.setnx(key, Thread.currentThread().getName()) == 1){ try{ System.out.println("线程("+Thread.currentThread().getName()+")获取到锁,开始执行操作"); count++; System.out.println(count); break; }finally{ System.out.println("操作执行完成,释放锁"); //操作执行完一定要释放锁,所以在finally块中执行 jedis.del(key); } }else{ //返回的不是1,说明已经有某个线程获取到了锁 try { //等待100毫秒之后重试 Thread.sleep(100l); } catch (InterruptedException e) { e.printStackTrace(); } } } }catch(Exception e){ e.printStackTrace(); }finally{ //释放Redis连接 jedis.disconnect(); } } }).start(); } } }
上述代码的输出结果为:
线程(Thread-320)获取到锁,开始执行操作 1 操作执行完成,释放锁 线程(Thread-463)获取到锁,开始执行操作 2 操作执行完成,释放锁 线程(Thread-997)获取到锁,开始执行操作 3 操作执行完成,释放锁 ... 线程(Thread-409)获取到锁,开始执行操作 998 操作执行完成,释放锁 线程(Thread-742)获取到锁,开始执行操作 999 操作执行完成,释放锁 线程(Thread-286)获取到锁,开始执行操作 1000 操作执行完成,释放锁
上述代码虽然是在单应用多线程情况下测试的,但即便是在分布式环境下多应用多线程去获取锁,结果依然是正确的。
三、解决死锁问题
之前的例子代码只是测试代码,只是为了说明原理,例子本身很简单,所以有一些考虑不周的地方。比如当获取到锁之后在业务操作执行过程中发生了环境问题导致断开了和Redis的连接,那就无法在finally块中释放锁,导致其他等待获取锁的线程无限等待下去,也就是发生了死锁现象。
解决方式:
可以在Redis中给锁设置一个过期时间,这样即便无法释放锁,锁也能在一段时间后自动释放。
代码上只需要在获取到锁之后在try语句块中加入如下代码:
jedis.expire(key, 10); //这里给锁设置10秒的过期时间
更妥善的解决方式:
第一个解决方式并不是很好,因为当业务操作处理时间很长,超过了设置的过期时间,那锁就自动释放了,然后再执行finally块中释放锁的操作时,这个锁可能已经被其他线程所持有,会导致把其他线程持有的锁给释放了,从而导致并发问题。所以更妥善一点的方式是在释放锁时判断一下锁是否已经过期,如果已经过期就不用再释放了。
代码上把获取到锁之后的操作改为如下代码:
long start = System.currentTimeMillis(); //获取起始时间毫秒数 try{ jedis.expire(key, 10); ... }finally{ ... if(System.currentTimeMillis() < start+10*1000){ //如果之前设置的锁还未过期,则释放掉 jedis.del(key); } }
以上是Java基于Redis实现分布式锁的详细内容。更多信息请关注PHP中文网其他相关文章!

本文讨论了使用Maven和Gradle进行Java项目管理,构建自动化和依赖性解决方案,以比较其方法和优化策略。

本文使用Maven和Gradle之类的工具讨论了具有适当的版本控制和依赖关系管理的自定义Java库(JAR文件)的创建和使用。

本文讨论了使用咖啡因和Guava缓存在Java中实施多层缓存以提高应用程序性能。它涵盖设置,集成和绩效优势,以及配置和驱逐政策管理最佳PRA

本文讨论了使用JPA进行对象相关映射,并具有高级功能,例如缓存和懒惰加载。它涵盖了设置,实体映射和优化性能的最佳实践,同时突出潜在的陷阱。[159个字符]

Java的类上载涉及使用带有引导,扩展程序和应用程序类负载器的分层系统加载,链接和初始化类。父代授权模型确保首先加载核心类别,从而影响自定义类LOA


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

适用于 Eclipse 的 SAP NetWeaver 服务器适配器
将Eclipse与SAP NetWeaver应用服务器集成。

禅工作室 13.0.1
功能强大的PHP集成开发环境

SecLists
SecLists是最终安全测试人员的伙伴。它是一个包含各种类型列表的集合,这些列表在安全评估过程中经常使用,都在一个地方。SecLists通过方便地提供安全测试人员可能需要的所有列表,帮助提高安全测试的效率和生产力。列表类型包括用户名、密码、URL、模糊测试有效载荷、敏感数据模式、Web shell等等。测试人员只需将此存储库拉到新的测试机上,他就可以访问到所需的每种类型的列表。

Dreamweaver CS6
视觉化网页开发工具

螳螂BT
Mantis是一个易于部署的基于Web的缺陷跟踪工具,用于帮助产品缺陷跟踪。它需要PHP、MySQL和一个Web服务器。请查看我们的演示和托管服务。