利用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返回的值是否大于当前时间来判断获取锁是否成功。如果小于当前时间,那获取成功。如果大于当前时间,说明已经有线程修改过了。再继续等待。
近期热议
推荐标签 标签
-
API
79 引用 • 431 回帖 • 1 关注
应用程序编程接口(Application Programming Interface)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。
-
安全
203 引用 • 818 回帖 • 2 关注
安全永远都不是一个小问题。
-
AngularJS
12 引用 • 50 回帖 • 503 关注
AngularJS 诞生于 2009 年,由 Misko Hevery 等人创建,后为 Google 所收购。是一款优秀的前端 JS 框架,已经被用于 Google 的多款产品当中。AngularJS 有着诸多特性,最为核心的是:MVC、模块化、自动化双向数据绑定、语义化标签、依赖注入等。2.0 版本后已经改名为 Angular。
-
游戏
180 引用 • 821 回帖 • 2 关注
沉迷游戏伤身,强撸灰飞烟灭。
-
大疆创新
2 引用 • 14 回帖
深圳市大疆创新科技有限公司(DJI-Innovations,简称 DJI),成立于 2006 年,是全球领先的无人飞行器控制系统及无人机解决方案的研发和生产商,客户遍布全球 100 多个国家。通过持续的创新,大疆致力于为无人机工业、行业用户以及专业航拍应用提供性能最强、体验最佳的革命性智能飞控产品和解决方案。
-
Hadoop
88 引用 • 122 回帖 • 622 关注
Hadoop 是由 Apache 基金会所开发的一个分布式系统基础架构。用户可以在不了解分布式底层细节的情况下,开发分布式程序。充分利用集群的威力进行高速运算和存储。
-
Flutter
39 引用 • 92 回帖 • 2 关注
Flutter 是谷歌的移动 UI 框架,可以快速在 iOS 和 Android 上构建高质量的原生用户界面。 Flutter 可以与现有的代码一起工作,它正在被越来越多的开发者和组织使用,并且 Flutter 是完全免费、开源的。
-
SSL
70 引用 • 193 回帖 • 415 关注
SSL(Secure Sockets Layer 安全套接层),及其继任者传输层安全(Transport Layer Security,TLS)是为网络通信提供安全及数据完整性的一种安全协议。TLS 与 SSL 在传输层对网络连接进行加密。
-
OpenShift
14 引用 • 20 回帖 • 653 关注
红帽提供的 PaaS 云,支持多种编程语言,为开发人员提供了更为灵活的框架、存储选择。
-
小薇
34 引用 • 467 回帖 • 759 关注
小薇是一个用 Java 写的 QQ 聊天机器人 Web 服务,可以用于社群互动。
由于 Smart QQ 从 2019 年 1 月 1 日起停止服务,所以该项目也已经停止维护了!
-
负能量
88 引用 • 1235 回帖 • 417 关注
上帝为你关上了一扇门,然后就去睡觉了....努力不一定能成功,但不努力一定很轻松 (° ー °〃)
-
快应用
15 引用 • 127 回帖 • 1 关注
快应用 是基于手机硬件平台的新型应用形态;标准是由主流手机厂商组成的快应用联盟联合制定;快应用标准的诞生将在研发接口、能力接入、开发者服务等层面建设标准平台;以平台化的生态模式对个人开发者和企业开发者全品类开放。
-
GAE
14 引用 • 42 回帖 • 807 关注
Google App Engine(GAE)是 Google 管理的数据中心中用于 WEB 应用程序的开发和托管的平台。2008 年 4 月 发布第一个测试版本。目前支持 Python、Java 和 Go 开发部署。全球已有数十万的开发者在其上开发了众多的应用。
-
QQ
45 引用 • 557 回帖
1999 年 2 月腾讯正式推出“腾讯 QQ”,在线用户由 1999 年的 2 人(马化腾和张志东)到现在已经发展到上亿用户了,在线人数超过一亿,是目前使用最广泛的聊天软件之一。
-
SQLServer
21 引用 • 31 回帖 • 1 关注
SQL Server 是由 [微软] 开发和推广的关系数据库管理系统(DBMS),它最初是由 微软、Sybase 和 Ashton-Tate 三家公司共同开发的,并于 1988 年推出了第一个 OS/2 版本。
-
Telegram
5 引用 • 35 回帖 • 1 关注
Telegram 是一个非盈利性、基于云端的即时消息服务。它提供了支持各大操作系统平台的开源的客户端,也提供了很多强大的 APIs 给开发者创建自己的客户端和机器人。
-
Q&A
9436 引用 • 42976 回帖 • 109 关注
提问之前请先看《提问的智慧》,好的问题比好的答案更有价值。
-
jsoup
6 引用 • 1 回帖 • 489 关注
jsoup 是一款 Java 的 HTML 解析器,可直接解析某个 URL 地址、HTML 文本内容。它提供了一套非常省力的 API,可通过 DOM,CSS 以及类似于 jQuery 的操作方法来取出和操作数据。
-
Webswing
1 引用 • 15 回帖 • 643 关注
Webswing 是一个能将任何 Swing 应用通过纯 HTML5 运行在浏览器中的 Web 服务器,详细介绍请看 将 Java Swing 应用变成 Web 应用 。
-
SendCloud
2 引用 • 8 回帖 • 490 关注
SendCloud 由搜狐武汉研发中心孵化的项目,是致力于为开发者提供高质量的触发邮件服务的云端邮件发送平台,为开发者提供便利的 API 接口来调用服务,让邮件准确迅速到达用户收件箱并获得强大的追踪数据。
-
安装
132 引用 • 1184 回帖
你若安好,便是晴天。
-
Ubuntu
127 引用 • 169 回帖 • 1 关注
Ubuntu(友帮拓、优般图、乌班图)是一个以桌面应用为主的 Linux 操作系统,其名称来自非洲南部祖鲁语或豪萨语的“ubuntu”一词,意思是“人性”、“我的存在是因为大家的存在”,是非洲传统的一种价值观,类似华人社会的“仁爱”思想。Ubuntu 的目标在于为一般用户提供一个最新的、同时又相当稳定的主要由自由软件构建而成的操作系统。
-
RemNote
2 引用 • 16 回帖 • 9 关注
-
GitHub
210 引用 • 2040 回帖 • 1 关注
GitHub 于 2008 年上线,目前,除了 Git 代码仓库托管及基本的 Web 管理界面以外,还提供了订阅、讨论组、文本渲染、在线文件编辑器、协作图谱(报表)、代码片段分享(Gist)等功能。正因为这些功能所提供的便利,又经过长期的积累,GitHub 的用户活跃度很高,在开源世界里享有深远的声望,并形成了社交化编程文化(Social Coding)。
-
Caddy
12 引用 • 54 回帖 • 163 关注
Caddy 是一款默认自动启用 HTTPS 的 HTTP/2 Web 服务器。
-
Bug
76 引用 • 1742 回帖
Bug 本意是指臭虫、缺陷、损坏、犯贫、窃听器、小虫等。现在人们把在程序中一些缺陷或问题统称为 bug(漏洞)。
-
友情链接
24 引用 • 373 回帖 • 1 关注
确认过眼神后的灵魂连接,站在链在!
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于