文章目录
前言
本文主要讲了分布式锁的原子性及实现Redis锁的两种方式。
提示:以下是本篇文章正文内容
一、分布式锁是什么?
分布式锁是控制分布式系统之间同步访问共享资源的一种方式。
(不同机器中不同线程对相同共享资源访问的控制。)
二、分布式锁的原子性
非原子性的分布式锁
代码如下(示例):
String s = RedisUtils.get(key);
if (StringUtils.isEmpty(s)) {
RedisUtils.setex(key, expireTime,expireTime);
return true;
}
return false;
整个设置锁过程分为两步,1、获取锁。2、不存在生成锁并设置过期时间。
加锁是两步操作,这并不能保证分布式锁的原子性。
高并发情况下,线程A和线程B几乎同时进入到加锁这一步,二者都没获取到锁,线程A先设置锁,并返回成功执行业务逻辑;线程B 也加锁成功执行业务逻辑,会造成严重的后果。
三、分布式锁的两种实现思路
1.普通分布式锁
代码如下(示例):
long setNx = RedisUtils.setnx(key, Long.toString(expireTime));
if (setNx ==1L) {
RedisUtils.expire(key, expireTime);
return true;
}
return false;
Redis单个命令具有原子性,直接用setNx (如果不存在,set进去)命令即可。
其实这也是两步,先设置值,再设置过期时间,但是:
还是刚才那种场景,setNx就不会出现线程A和线程B同时设置key了,必然会有一个线程设置成功,另一个线程设置失败,最终设置过期时间的只会是成功的那个线程。
2.自旋竞争锁
//线程刚进来的时间
long start = System.currentTimeMillis();
for(;;){
//SET命令返回OK ,则证明获取锁成功
long setNx = RedisUtils.setnx(key, Long.toString(expireTime));
if (setNx ==1L) {
RedisUtils.expire(key, expireTime);
return true;
}
//一直竞争不到锁也不是个事,我们一般设置过期时间内未得到锁,直接退出
//到结束中间经历的时间
long end = System.currentTimeMillis() - start;
if (end >= expireTime * 1000L) {
return false;
}
// 线程等待释放锁,以便下一个for循环竞争锁
LockSupport.parkNanos(1000* 1000 * 1000);
}
其实就是,当线程开始没有获取到锁的时候,并不立即退出,而是通过无线for循环不断的去竞争锁。
总结
关于上面两种加锁方式使用的业务场景我还有点模糊,欢迎评论区大佬补充。
它两最大的区别就是一个立即退出,另一个等一段时间竞争锁直到竞争不到才退出。