利用redis来实现分布式锁。一般就用setnx和getset两个命令。
>* `NX`是`Not eXists`的缩写,如`SETNX`命令就应该理解为:`SET if Not eXists`.
>* `getset`是同步的
java之jedis实现
`expireMsecs` 锁持有超时,防止线程在入锁以后,无限的执行下去,让锁无法释放
`timeoutMsecs` 锁等待超时,防止线程饥饿,永远没有入锁执行代码的机会
代码如下:
```
private int timeoutMsecs
private String lockKey
private static long expireMsecs = 1000 * 60 * 5 // min 锁持有超时
// timeoutMsecs 表示锁等待超时
public JedisLock(Integer timeoutMsecs, String lockKey) {
this.timeoutMsecs = timeoutMsecs
this.lockKey = lockKey
}
public synchronized boolean acquire(Jedis jedis) throws InterruptedException {
int timeout = timeoutMsecs
while (timeout >= 0) {
long expires = System.currentTimeMillis() + expireMsecs + 1
String expiresStr = String.valueOf(expires) //锁到期时间
if (jedis.setnx(lockKey, expiresStr) == 1) {
return true
}
String currentValueStr = jedis.get(lockKey); //redis里的时间
// 表示已经锁失效,要重新设置锁
if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) {
//判断是否为空,不为空的情况下,如果被其他线程设置了值,则第二个条件判断是过不去的
// lock is expired
String oldValueStr = jedis.getSet(lockKey, expiresStr)
//获取上一个锁到期时间,并设置现在的锁到期时间,
//只有一个线程才能获取上一个线上的设置时间,因为jedis.getSet是同步的
if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {
//如过这个时候,多个线程恰好都到了这里,但是只有一个线程的设置值和当前值相同,他才有权利获取锁
return true
}
}
timeout -= 100
Thread.sleep(100)
}
return false
}
```
所以大致的思路就是
>* 先定义好一个锁等待时间和锁超时时间,我们使用中分别设置了300ms和5min,
>* 线程执行进方法后,设置锁到期时间为当前时间加5分钟,就是`System.currentTimeMillis() + 1000*60*5 + 1`
>* 执行setnx,执行完返回1就是获取锁成功。返回0时,表示未获取到锁,执行下面一步
>* 判断是否已经锁到期,`currentValueStr`和当前时间比较,如果未过期,锁等待时间减100ms,sleep一下继续重复这个动作。如果已经到期,执行下面一步。
>* 通过`getSet`把redis中的锁超时时间设为自己的时间(因此自己要获取锁),这个方法是同步的。保证只能有个线程能拿到锁。执行这步返回的值是旧值,
>* 判断旧值和之前的锁超时的值是否一致。一致则获取锁成功。失败的话锁等待时间减1,sleep一下继续重复之前的逻辑。
再总结一下:
> 用最简单的办法就是,多个进程执行以下Redis命令:SETNX lock.foo
如果 SETNX 返回1,说明该进程获得锁,SETNX将键 lock.foo 的值设置为锁的超时时间(当前时间 + 锁的有效时间)。
如果 SETNX 返回0,说明其他进程已经获得了锁,进程不能进入临界区。进程可以在一个循环中不断地尝试 SETNX 操作,以获得锁。
但是这里有个问题,如何解决死锁,
> 考虑一种情况,如果进程获得锁后,断开了与 Redis 的连接(可能是进程挂掉,或者网络中断),如果没有有效的释放锁的机制,那么其他进程都会处于一直等待的状态,即出现“死锁”。
锁超时时,我们不能简单地使用 DEL 命令删除键 lock.foo 以释放锁。因为这样可能同时会有多个线程获得锁。
比如就是p2,p3两个进程同时发现锁已经超时。p2执行删除操作,添加自己的key,返回1,获取锁成功。接着p3又删除p2设置的值,自己获取锁。这样子显然是不行的。
为了解决上述算法可能出现的多个进程同时获得锁的问题,我们再来看以下的算法。
> 超时时我们可以这样处理。比如p4,p5发现超时之后,通过getset去更新key值,这个是同步的,能保证线程安全。然后根据getset返回的值是否大于当前时间来判断获取锁是否成功。如果小于当前时间,那获取成功。如果大于当前时间,说明已经有线程修改过了。再继续等待。
近期热议
推荐标签 标签
-
ReactiveX
1 引用 • 2 回帖 • 153 关注
ReactiveX 是一个专注于异步编程与控制可观察数据(或者事件)流的 API。它组合了观察者模式,迭代器模式和函数式编程的优秀思想。
-
WiFiDog
1 引用 • 7 回帖 • 585 关注
WiFiDog 是一套开源的无线热点认证管理工具,主要功能包括:位置相关的内容递送;用户认证和授权;集中式网络监控。
-
GitHub
209 引用 • 2031 回帖
GitHub 于 2008 年上线,目前,除了 Git 代码仓库托管及基本的 Web 管理界面以外,还提供了订阅、讨论组、文本渲染、在线文件编辑器、协作图谱(报表)、代码片段分享(Gist)等功能。正因为这些功能所提供的便利,又经过长期的积累,GitHub 的用户活跃度很高,在开源世界里享有深远的声望,并形成了社交化编程文化(Social Coding)。
-
V2Ray
1 引用 • 15 回帖
-
JSON
52 引用 • 190 回帖
JSON (JavaScript Object Notation)是一种轻量级的数据交换格式。易于人类阅读和编写。同时也易于机器解析和生成。
-
SOHO
7 引用 • 55 回帖 • 18 关注
为成为自由职业者在家办公而努力吧!
-
Log4j
20 引用 • 18 回帖 • 30 关注
Log4j 是 Apache 开源的一款使用广泛的 Java 日志组件。
-
Vue.js
264 引用 • 665 回帖
Vue.js(读音 /vju ː/,类似于 view)是一个构建数据驱动的 Web 界面库。Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。
-
RYMCU
4 引用 • 6 回帖 • 52 关注
RYMCU 致力于打造一个即严谨又活泼、专业又不失有趣,为数百万人服务的开源嵌入式知识学习交流平台。
-
CongSec
1 引用 • 1 回帖 • 10 关注
本标签主要用于分享网络空间安全专业的学习笔记
-
HTML
107 引用 • 295 回帖 • 2 关注
HTML5 是 HTML 下一个的主要修订版本,现在仍处于发展阶段。广义论及 HTML5 时,实际指的是包括 HTML、CSS 和 JavaScript 在内的一套技术组合。
-
jsoup
6 引用 • 1 回帖 • 482 关注
jsoup 是一款 Java 的 HTML 解析器,可直接解析某个 URL 地址、HTML 文本内容。它提供了一套非常省力的 API,可通过 DOM,CSS 以及类似于 jQuery 的操作方法来取出和操作数据。
-
人工智能
132 引用 • 188 回帖
人工智能(Artificial Intelligence)是研究、开发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门技术科学。
-
Unity
25 引用 • 7 回帖 • 186 关注
Unity 是由 Unity Technologies 开发的一个让开发者可以轻松创建诸如 2D、3D 多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。
-
安全
199 引用 • 816 回帖
安全永远都不是一个小问题。
-
程序员
565 引用 • 3532 回帖
程序员是从事程序开发、程序维护的专业人员。
-
Postman
4 引用 • 3 回帖 • 2 关注
Postman 是一款简单好用的 HTTP API 调试工具。
-
PHP
179 引用 • 407 回帖 • 489 关注
PHP(Hypertext Preprocessor)是一种开源脚本语言。语法吸收了 C 语言、 Java 和 Perl 的特点,主要适用于 Web 开发领域,据说是世界上最好的编程语言。
-
Tomcat
162 引用 • 529 回帖 • 4 关注
Tomcat 最早是由 Sun Microsystems 开发的一个 Servlet 容器,在 1999 年被捐献给 ASF(Apache Software Foundation),隶属于 Jakarta 项目,现在已经独立为一个顶级项目。Tomcat 主要实现了 JavaEE 中的 Servlet、JSP 规范,同时也提供 HTTP 服务,是市场上非常流行的 Java Web 容器。
-
微服务
96 引用 • 155 回帖
微服务架构是一种架构模式,它提倡将单一应用划分成一组小的服务。服务之间互相协调,互相配合,为用户提供最终价值。每个服务运行在独立的进程中。服务于服务之间才用轻量级的通信机制互相沟通。每个服务都围绕着具体业务构建,能够被独立的部署。
-
frp
20 引用 • 7 回帖 • 2 关注
frp 是一个可用于内网穿透的高性能的反向代理应用,支持 TCP、UDP、 HTTP 和 HTTPS 协议。
-
Telegram
5 引用 • 35 回帖
Telegram 是一个非盈利性、基于云端的即时消息服务。它提供了支持各大操作系统平台的开源的客户端,也提供了很多强大的 APIs 给开发者创建自己的客户端和机器人。
-
架构
142 引用 • 442 回帖
我们平时所说的“架构”主要是指软件架构,这是有关软件整体结构与组件的抽象描述,用于指导软件系统各个方面的设计。另外还有“业务架构”、“网络架构”、“硬件架构”等细分领域。
-
Typecho
12 引用 • 65 回帖 • 453 关注
Typecho 是一款博客程序,它在 GPLv2 许可证下发行,基于 PHP 构建,可以运行在各种平台上,支持多种数据库(MySQL、PostgreSQL、SQLite)。
-
创造
176 引用 • 995 回帖 • 1 关注
你创造的作品可能会帮助到很多人,如果是开源项目的话就更赞了!
-
golang
497 引用 • 1387 回帖 • 294 关注
Go 语言是 Google 推出的一种全新的编程语言,可以在不损失应用程序性能的情况下降低代码的复杂性。谷歌首席软件工程师罗布派克(Rob Pike)说:我们之所以开发 Go,是因为过去 10 多年间软件开发的难度令人沮丧。Go 是谷歌 2009 发布的第二款编程语言。
-
星云链
3 引用 • 16 回帖 • 2 关注
星云链是一个开源公链,业内简单的将其称为区块链上的谷歌。其实它不仅仅是区块链搜索引擎,一个公链的所有功能,它基本都有,比如你可以用它来开发部署你的去中心化的 APP,你可以在上面编写智能合约,发送交易等等。3 分钟快速接入星云链 (NAS) 测试网
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于