盒子
盒子
文章目录
  1. 前言
  2. 分布式锁
    1. 基于 redis 的分布式锁实现
  3. SETNX
    1. 加锁
    2. 获取锁
  4. Error
    1. 死锁

redis 的分布式锁实现

前言

详细 :

https://juejin.im/entry/5a502ac2518825732b19a595

这里只谈谈思路…

分布式锁

由于分布式系统的分布性,即多线程和多进程分布在不同的服务器中,通常的锁就没有用武之地了.

比如 Java 的 synchronized 和 lock,只能保证进程内线程间共享资源.

所以需要自己实现分布式锁…

常见方案:

  • 基于数据库实现分布式锁

    基于数据库表的增删

    基于数据库排它锁

  • 基于 zookeeper 实现分布式锁

  • 基于缓存实现分布式锁 如 redis

基于 redis 的分布式锁实现

需要自己实现锁!!!

想一想,实现一个锁应当如何做?

关键点是,同一个资源在同一时刻只能被一个线程访问.

如何做?

我们可以用一个标记来保存资源的状态(是否被线程占用)…

比如 jdk 的 Lock 的实现类里用到了 同步器

比如,我们可以假定有这个标记就代表资源被占用,没有这个标记,就代表资源尚未被占用.

另外,这个标记必须每个线程都能看到.(可见性)

redis 可以利用 SETNX 命令来做这个标记.

来看看 SETNX…

http://doc.redisfans.com/string/setnx.html

SETNX

SETNX key value

key 的值设为 value ,当且仅当 key 不存在。

若给定的 key 已经存在,则 SETNX 不做任何动作。

SETNX 是『SET if Not eXists』(如果不存在,则 SET)的简写。

  • 可用版本:

    = 1.0.0

  • 时间复杂度:

    O(1)

  • 返回值:

    设置成功,返回 1 。设置失败,返回 0

如上所说,当已存在 SETNX 设置的标记,说明已有线程获取锁.如果没有标记,则设置一个标记,代表当前线程获取到锁.

实际使用可能是这样 : 利用该命令设置一个 锁 与 锁的超时时间 的对应键值对.

SETNX lock.id lock.time

这里用 id 来区分锁.

如果设置成功,返回 1 .我们把这当作获取到锁.

如果设置失败,则说明别的线程先设置了,我们把这当作获取锁失败.

获取锁,可以用一个循环不断尝试…(在设置的超时时间内)

而解锁,就相应的删除该键值对即可.

DEL lock.id

加锁

简单的一段伪代码

1
2
3
4
5
6
7
8
9
public boolean tryLock(...){
if(SETNX key value 返回 1){
// ...
return true;
}else{
// ...
return false;
}
}

获取锁

1
2
3
4
5
6
7
8
9
10
11
12
public boolean(...){
while(true){
// 不断尝试获取锁
boolean hasLock = tryLock();
if(hasLock){
// 获取锁成功
return true;
}else{
...
}
}
}

为了避免死循环,得设置一个超时时间..

Error

死锁

分布式环境,如果一个线程获取了锁却断线了,其它试图获取锁的线程就会阻塞…