看到很多人手写分布式锁。事实上,SpringBoot开箱即用地完成了足够好的工作。支持主流的Redis和Zookeeper中间件,也支持JDBC。本文栈长以Redis为例(这也是使用最多的方案),教大家如何使用SpringBoot集成Redis实现缓存,以及如何简单快速的实现Redis分布式锁。分布式锁介绍SpringBoot在spring-integration项目中实现了Redis分布式锁,参考:https://docs.spring.io/spring-integration/docs/5.3.1.RELEASE/reference/html/redis.html#redis-lock-registry先看LockRegistry锁注册接口的所有实现类的结构图:DefaultLockRegistry是一个纯单机可重入锁,PassThruLockRegistry是一个空的实现类,没有任何使用价值。SpringIntegration4.0引入了基于Redis的分布式锁:RedisLockRegistry,从5.0开始实现了ExpirableLockRegistry接口,去除超时锁和无用锁。分布式锁实战添加依赖上面说了SpringBoot在spring-integration项目中实现了Redis分布式锁,所以需要这三个依赖:spring-boot-starter-data-redisspring-boot-starter-integrationspring-integration-redisorg.springframework.bootspring-boot-starter-data-redisorg.springframework.bootspring-boot-starter-integrationorg.springframework.integrationspring-integration-redisSpringBoot的基础知识就不介绍了。不熟悉的可以关注公众号Java技术栈,后台回复:boot,可以看看我写的历史实战教程。配置分布式锁@Bean(destroyMethod=”destroy”){for(inti=0;i<10;i++){newThread(()->{redisLockService.lock(key);try{Thread.sleep(3000L);}catch(InterruptedExceptione){e.printStackTrace();}System.out.println(DateFormatUtils.format(newDate(),"yyyy-MM-ddHH:mm:ss"));redisLockService.unlock(key);}).start();}return"OK";}RedisLockService是我封装的一个Redis锁服务。代码很多,这里就不贴了。完整的代码示例在Github上。可以加星标:https://github.com/javastacks/spring-boot-best-练习测试:http://localhost:8080/redis/lock?key=yeah输出:2020-06-2311:15:342020-06-2311:15:372020-06-2311:15:402020-06-2311:15:432020-06-2311:15:462020-06-2311:15:492020-06-2311:15:522020-06-2311:15:552020-06-2311:15:582020-06-2311:16:01可以看到每个该线程需要等待前一个线程休眠3秒后才能获取到锁源码分析集成,才会使用。RedisLockRegistry的源码你得去研究,不然遇到什么坑就得踩另一篇文章了。RedisLockRegistry有两个类构造函数:connectionFactory:Redis连接工厂registryKey:锁前缀expireAfter:过期时间(非必须,默认60秒)所以我们需要注册expireAfter选项,默认60秒是否满足业务需求,如果超过默认60或更少的时间,否则锁将失效。还有两个与RedisLockRegistry相关的重要成员变量:privatefinalStringclientId=UUID.randomUUID().toString();privatefinalMaplocks=newConcurrentHashMap<>();clientId:首先用于标识当前的RedisLockRegistry实例ID,设置和移除锁时会用到判断是否为当前的锁注册实例。locks:用于在内存中缓存当前锁注册实例的所有锁对象。获取锁对象在获取锁对象的源码中是这样的:内存中的每个key(ConcurrentHashMap)对应一个锁对象,如果已经生成锁对象则直接返回,如果没有则直接返回会生成然后返回,只有有了这个锁对象才能上传加锁和解锁操作。这个锁对象(RedisLock)其实实现了Java中的java.util.concurrent.locks.Lock锁接口:锁对象(RedisLock)还有两个很重要的成员变量:privatefinalReentrantLocklocalLock=newReentrantLock();privatevolatilelonglockedAt;localLock:localLock是本地内存可重入锁。每次去Redis加锁,都要先用本地的localLock加锁。这样就可以尽可能少的对Redis加锁,一方面也可以提高加锁的性能。lockedAt:lockedAt锁定时间,它将用于移除过时的锁。阻塞加锁RedisLock#lock():每100毫秒尝试获取一次锁,直到获取锁成功。不接受中断异常。当遇到其他异常时,会释放本地锁,返回异常,结束。主要看设置Redis锁的Lua脚本:根据key:clientId查询其对应的值。如果与当前clientId一致,则延长过期时间。如果clientId不存在,则直接锁定。如果以上不正确,则返回false。这样做的好处是可以将整个RedisLua脚本作为一个原子来执行,而不管并发和事务的影响。好了,核心源码的分析就结束了,其实也很简单。如果你不明白或者有兴趣,你可以进一步研究它。本文完整示例源码和之前SpringBoot快速集成Redis的示例代码已经上传至Github。欢迎Star关注学习。https://github.com/javastacks/spring-boot-best-practice那么,你还在手写分布式锁吗?快起来!