/**
* Created by BingZhong on 2017/7/29.
*
* 基于Redis实现的分布式锁
*/
public final class RedisLockHelper {
private static Logger logger = LoggerFactory.getLogger(RedisLockHelper.class);
/**
* redis操作帮助类,可以是其他封装了redis操作的类
*/
private RedisHelper redisHelper;
public static final long DEFAULT_TIMEOUT = 30 * 1000;
public static final long DEFAULT_SLEEP_TIME = 100;
private RedisLockHelper(RedisHelper redisHelper) {
this.redisHelper = redisHelper;
}
public static RedisLockHelper getInstance(RedisHelper redisHelper) {
return new RedisLockHelper(redisHelper);
}
/**
* 创建锁
*
* @param mutex 互斥量
* @param timeout 锁的超时时间
* @param sleepTime 线程自旋尝试获取锁时的休眠时间
* @param timeUnit 时间单位
*/
public RedisLock newLock(String mutex, long timeout, long sleepTime, TimeUnit timeUnit) {
logger.info("创建分布式锁,互斥量为{}", mutex);
return new RedisLock(mutex, timeout, sleepTime, timeUnit);
}
public RedisLock newLock(String mutex, long timeout, TimeUnit timeUnit) {
return newLock(mutex, timeout, DEFAULT_SLEEP_TIME, timeUnit);
}
public RedisLock newLock(String mutex) {
return newLock(mutex, DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS);
}
public class RedisLock {
/**
* 用于创建redis健值对的键,相当于互斥量
*/
private final String mutex;
/**
* 锁过期的绝对时间
*/
private volatile long lockExpiresTime = 0;
/**
* 锁的超时时间
*/
private final long timeout;
/**
* 每次循环获取锁的休眠时间
*/
private final long sleepTime;
/**
* 锁的线程持有者
*/
private volatile Thread lockHolder = null;
private final ReentrantLock threadLock = new ReentrantLock();
public RedisLock(String mutex, long timeout, long sleepTime, TimeUnit timeUnit) {
this.mutex = mutex;
this.timeout = timeUnit.toMillis(timeout);
this.sleepTime = timeUnit.toMillis(sleepTime);
}
/**
* 加锁,将会一直尝试获取锁,直到超时
*/
public boolean lock(long acquireTimeout, TimeUnit timeUnit) throws InterruptedException {
acquireTimeout = timeUnit.toMillis(acquireTimeout);
long acquireTime = acquireTimeout + System.currentTimeMillis();
threadLock.tryLock(acquireTimeout, timeUnit);
try {
while (true) {
boolean hasLock = tryLock();
if (hasLock) {
//获取锁成功
return true;
} else if (acquireTime < System.currentTimeMillis()) {
break;
}
Thread.sleep(sleepTime);
}
} finally {
if (threadLock.isHeldByCurrentThread()) {
threadLock.unlock();
}
}
return false;
}
/**
* 尝试获取锁,无论是否获取到锁都将直接返回而不会阻塞
* 不支持重入锁
*/
public boolean tryLock() {
if (lockHolder == Thread.currentThread()) {
throw new IllegalMonitorStateException("不支持重入锁");
}
long currentTime = System.currentTimeMillis();
String expires = String.valueOf(timeout + currentTime);
//尝试设置互斥量
if (redisHelper.setNx(mutex, expires) > 0) {
setLockStatus(expires);
return true;
} else {
String currentLockTime = redisHelper.get(mutex);
//检查锁是否超时
if (Objects.nonNull(currentLockTime) && Long.parseLong(currentLockTime) < currentTime) {
//获取旧的锁时间并设置互斥量
String oldLockTime = redisHelper.getSet(mutex, expires);
//判断获取到的旧值是否一致,不一致证明已经有另外的进程(线程)成功获取到了锁
if (Objects.nonNull(oldLockTime) && Objects.equals(oldLockTime, currentLockTime)) {
setLockStatus(expires);
return true;
}
}
return false;
}
}
/**
* 该锁是否被锁住
*/
public boolean isLock() {
String currentLockTime = redisHelper.get(mutex);
//存在互斥量且锁还为过时即锁住
return Objects.nonNull(currentLockTime) && Long.parseLong(currentLockTime) > System.currentTimeMillis();
}
public String getMutex() {
return mutex;
}
/**
* 解锁
*/
public boolean unlock() {
//只有锁的持有线程才能解锁
if (lockHolder == Thread.currentThread()) {
//判断锁是否超时,没有超时才将互斥量删除
if (lockExpiresTime > System.currentTimeMillis()) {
redisHelper.del(mutex);
logger.info("删除互斥量[{}]", mutex);
}
lockHolder = null;
logger.info("释放[{}]锁成功", mutex);
return true;
} else {
throw new IllegalMonitorStateException("没有获取到锁的线程无法执行解锁操作");
}
}
private void setLockStatus(String expires) {
lockExpiresTime = Long.parseLong(expires);
lockHolder = Thread.currentThread();
logger.info("获取[{}]锁成功", mutex);
}
}
}
分享到:
相关推荐
虽然网上已经有各种介绍Redis分布式锁实现的博客,然而他们的实现却有着各种各样的问题,为了避免误人子弟,本篇博客将详细介绍如何正确地实现Redis分布式锁。 可靠性 首先,为了确保分布式锁可用,我们至少要确保...
1. RedLock算法:它通过多个独立的Redis节点来实现锁的可靠性,增加锁的抗故障能力。使用RedLock算法需要多个Redis实例,客户端必须获取到超过半数的锁才能认为成功获取锁。但RedLock在某些极端情况下可能无法保证锁...
Redis提供`SETNX`(Set If Not Exists)和`EXPIRE`命令组合,可以创建临时且唯一的键,从而实现锁的功能。此外,利用Lua脚本的原子执行特性,可以进一步确保锁操作的完整性。 其次,Redis的发布/订阅(Pub/Sub)...
Redis是一个内存数据结构存储系统,其速度非常快,适合实现锁。使用Redis实现分布式锁通常有两种方法:`SETNX`命令和`lua`脚本。`SETNX`命令在键不存在时设置键值,实现互斥锁。为了防止锁不能被释放(例如,客户端...
实现分布式 Session 首先需要安装相应的包,例如对于 Redis,可以使用 Microsoft.AspNetCore.Session.Redis 包。 1.3.1. 添加依赖 在项目的 `csproj` 文件中添加对 Redis 的 NuGet 包引用: ```xml ``` 替换 ...
在分布式缓存技术的研究与实现中,Redis作为一种高性能的键值存储数据库,广泛应用于分布式缓存领域。它支持多种数据结构,如字符串、哈希、列表、集合等,适用于不同的应用场景。分布式缓存的主要配置方式有主从...
在实践中,基于Redis的分布式锁方案被广泛采用,利用SETNX命令确保在key不存在时设置值,并通过设置过期时间来控制锁的生命周期。然而,这种方案并非无懈可击,存在如单点故障、锁时间不可控和无法自动续租等问题,...
7. 分布式特性:Redis支持数据的主从复制和高可用集群。这使得Redis能够适用于大规模数据存储和高并发访问,增加了系统的可靠性和稳定性。 8. 序列化:在项目应用中,涉及到将数组等非字符串类型的数据存储到Redis...
为了解决这个问题,ASP.NET Core 提供了分布式 Session,它能够将 Session 数据存储在分布式缓存中,如 Redis 或 SQL Server,以实现跨服务器的数据共享。 **1. 分布式 Session 的工作原理** 分布式 Session 利用...
* 分布式架构:在分布式架构中,Redis 的发布订阅机制可以用于实现数据的实时同步。 * 博客网站:在博客网站中,使用 Redis 的发布订阅机制,可以实时推送新文章给订阅者。 五、总结 本篇文章详细介绍了 Redis 的...
浅谈高并发下接口幂等性解决方案 幂等性是在编程中一个非常重要的概念,它是指一个操作,不论执行多少次,产生的效果和返回的结果都是一样的。幂等函数或幂等方法是指可以使用相同参数重复执行,并能获得相同结果的...
#### 三、Redis迭代演化和Redis7新特性浅谈 - **时间推移与版本升级**: - 官方博客: - 版本迭代历程中的一些重要里程碑。 - **Redis7.0新特性**: - **Redis Functions**: 提供函数调用的能力,扩展Redis的功能...
这篇“缓存技术浅谈”可能是一篇深入解析缓存概念、原理及应用的博客文章,结合PPT形式提供了更直观的理解。以下是根据标题和描述可能涉及的一些核心知识点: 1. **缓存基本概念**:缓存是一种存储技术,用于临时...
缓存雪崩则是大量缓存同时失效,造成系统崩溃,可以通过设置合理的过期时间、加锁机制或者分布式锁来避免。 总的来说,网站架构中的缓存应用是一门深奥的学问,需要根据业务场景和性能需求,选择合适的缓存类型、...
标题:“Web应用服务器缓存浅谈PPT” 在Web开发中,服务器缓存是一种提高性能和响应速度的关键技术。本讲座将探讨Web应用服务器缓存的基础知识,包括其工作原理、常见类型和最佳实践。通过学习,我们可以更好地理解...