java 从零实现属于你的 redis 分布式锁

点赞再看 , 已成习惯 。
java 从零实现属于你的 redis 分布式锁文章插图
redis分布式锁
为什么需要分布式锁在 jdk 中为我们提供了加锁的方式:
(1)synchronized 关键字
(2)volatile + CAS 实现的乐观锁
(3)ReadWriteLock 读写锁
(4)ReenTrantLock 可重入锁
等等 , 这些锁为我们变成提供极大的便利性 , 保证在多线程的情况下 , 保证线程安全 。
但是在分布式系统中 , 上面的锁就统统没用了 。
我们想要解决分布式系统中的并发问题 , 就需要引入分布式锁的概念 。
java 从零实现属于你的 redis 分布式锁文章插图
分布式锁
上一节我们已经对分布式锁原理进行了详细讲解 , 参见:
redis 分布式锁原理详解
java 代码实现创作动机首先是对锁实现原理的一个实现 , 理论指导实践 , 实践完善理论 。
晚上关于 redis 分布式锁的文章一大堆 , 但是也都稂莠不齐 。
redis 分布式锁工具有时候中间件团队不见得会提供 , 提供了也不见得经常维护 , 不如自己实现一个 , 知道原理 , 也方便修改 。
接口定义为了便于和 JDK 复用 , 我们让接口继承自 jdk 的 Lock 接口 。
package com.github.houbb.lock.api.core;import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.Lock;/** * 锁定义 * @author binbin.hou * @since 0.0.1 */public interface ILock extends Lock {/*** 尝试加锁* @param time 时间* @param unit 当为* @param key key* @return 返回* @throws InterruptedException 异常* @since 0.0.1*/boolean tryLock(long time, TimeUnit unit,String key) throws InterruptedException;/*** 尝试加锁* @param key key* @return 返回* @since 0.0.1*/boolean tryLock(String key);/*** 解锁* @param key key* @since 0.0.1*/void unlock(String key);}方法我们只添加了三个比较常用的核心方法 , 作为第一个版本 , 简单点 。
后续陆续添加即可 。
抽象实现为了便于后期添加更多的所实现 , 这里首先实现了一个公用的抽象父类 。
package com.github.houbb.lock.redis.core;import com.github.houbb.lock.api.core.ILock;import com.github.houbb.lock.redis.constant.LockRedisConst;import com.github.houbb.wait.api.IWait;import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.Condition;/** * 抽象实现 * @author binbin.hou * @since 0.0.1 */public abstract class AbstractLockRedis implements ILock {/*** 锁等待* @since 0.0.1*/private final IWait wait;protected AbstractLockRedis(IWait wait) {this.wait = wait;}@Overridepublic void lock() {throw new UnsupportedOperationException();}@Overridepublic void lockInterruptibly() throws InterruptedException {throw new UnsupportedOperationException();}@Overridepublic boolean tryLock() {return tryLock(LockRedisConst.DEFAULT_KEY);}@Overridepublic void unlock() {unlock(LockRedisConst.DEFAULT_KEY);}@Overridepublic boolean tryLock(long time, TimeUnit unit, String key) throws InterruptedException {long startTimeMills = System.currentTimeMillis();// 一次获取 , 直接成功boolean result = this.tryLock(key);if(result) {return true;}// 时间判断if(time <= 0) {return false;}long durationMills = unit.toMillis(time);long endMills = startTimeMills + durationMills;// 循环等待while (System.currentTimeMillis() < endMills) {result = tryLock(key);if(result) {return true;}// 等待 10mswait.wait(TimeUnit.MILLISECONDS, 10);}return false;}@Overridepublic synchronized boolean tryLock(long time, TimeUnit unit) throws InterruptedException {return tryLock(time, unit, LockRedisConst.DEFAULT_KEY);}@Overridepublic Condition newCondition() {throw new UnsupportedOperationException();}}最核心的实际上是 public boolean tryLock(long time, TimeUnit unit, String key) throws InterruptedException 方法 。
这个方法会调用 this.tryLock(key) 获取锁 , 如果成功 , 直接返回;如果不成功 , 则循环等待 。
这里设置了超时时间 , 如果超时 , 则直接返回 true 。
redis 锁实现我们实现的 redis 分布锁 , 继承自上面的抽象类 。
package com.github.houbb.lock.redis.core;import com.github.houbb.heaven.util.lang.StringUtil;import com.github.houbb.id.api.Id;import com.github.houbb.id.core.util.IdThreadLocalHelper;import com.github.houbb.lock.redis.constant.LockRedisConst;import com.github.houbb.lock.redis.exception.LockRedisException;import com.github.houbb.lock.redis.support.operator.IOperator;import com.github.houbb.wait.api.IWait;/** * 这里是基于 redis 实现 * * 实际上也可以基于 zk/数据库等实现 。* * @author binbin.hou * @since 0.0.1 */public class LockRedis extends AbstractLockRedis {/*** redis 操作实现* @since 0.0.1*/private final IOperator redisOperator;/*** 主键标识* @since 0.0.1*/private final Id id;public LockRedis(IWait wait, IOperator redisOperator, Id id) {super(wait);this.redisOperator = redisOperator;this.id = id;}@Overridepublic boolean tryLock(String key) {final String requestId = id.id();IdThreadLocalHelper.put(requestId);return redisOperator.lock(key, requestId, LockRedisConst.DEFAULT_EXPIRE_MILLS);}@Overridepublic void unlock(String key) {final String requestId = IdThreadLocalHelper.get();if(StringUtil.isEmpty(requestId)) {String threadName = Thread.currentThread().getName();throw new LockRedisException("Thread " + threadName +" not contains requestId");}boolean unlock = redisOperator.unlock(key, requestId);if(!unlock) {throw new LockRedisException("Unlock key " + key + " result is failed!");}}}