自己做的网站绑定域名,网站备案授权书,做名片模板网站,网页创建基本步骤一、前言 首先我们先考虑以下这个问题#xff1a;
在多线程并发的情况下#xff0c;我们如何保证一个代码块在同一时间只能由一个线程访问呢#xff1f;
答案#xff1a;通常来说#xff0c;我们可以用锁来保证。比如java的synchronized用法以及ReentrantLock等等。这样…一、前言 首先我们先考虑以下这个问题
在多线程并发的情况下我们如何保证一个代码块在同一时间只能由一个线程访问呢
答案通常来说我们可以用锁来保证。比如java的synchronized用法以及ReentrantLock等等。这样就可以保证同一个JVM进程内的多个线程同步执行。
那么如果在分布式的集群环境中如何保证不同结点的线程同步执行呢
对于分布式场景我们可以尝试用分布式锁。
二、分布式锁的实现有哪些呢a.Memcached分布式锁
利用Memcached的add命令。此命令是原子性操作只有在key不存在的情况下才能add成功也就意味着线程得到了锁。
b.Redis分布式锁
和Memcached的方式类似利用Redis的setnx命令。此命令同样是原子性操作只有在key不存在的情况下才能set成功。setnx命令并不完善后续会介绍替代方案
c.Zookeeper分布式锁
利用Zookeeper的顺序临时节点来实现分布式锁和等待队列。Zookeeper设计的初衷就是为了实现分布式锁服务的。
三、如何用Redis来实现分布式锁呢3.1 分布式锁的三个核心要素1.加锁 使用setnx来加锁。key是锁的唯一标识按业务来决定命名value这里设置为test。
setx key test
当一个线程执行setnx返回1说明key原本不存在该线程成功得到了锁当一个线程执行setnx返回0说明key已经存在该线程抢锁失败
2.解锁 有加锁就得有解锁。当得到的锁的线程执行完任务需要释放锁以便其他线程可以进入。释放锁的最简单方式就是执行del指令。
del key
释放锁之后其他线程就可以继续执行setnx命令来获得锁。
3.锁超时 锁超时知道的是如果一个得到锁的线程在执行任务的过程中挂掉来不及显式地释放锁这块资源将会永远被锁住别的线程北向进来。
所以setnx的key必须设置一个超时时间以保证即使没有被显式释放这把锁也要在一段时间后自动释放。setnx不支持超时参数所以需要额外指令
expire key 30
3.2 上述分布式锁存在的问题 通过上述setnx 、del和expire实现的分布式锁还是存在着一些问题。下面将具体举例说明该问题
1.setnx和expire的非原子性问题 假设一个场景中某一个线程刚执行setnx成功得到了锁。此时setnx刚执行成功还未来得及执行expire命令节点就挂掉了。此时这把锁就没有设置过期时间别的线程就再也无法获得该锁。
解决措施:
由于setnx指令本身是不支持传入超时时间的而在Redis2.6.12版本上为set指令增加了可选参数, 用法如下
SET key value [EX seconds][PX milliseconds] [NX|XX]
EX second: 设置键的过期时间为second秒 PX millisecond设置键的过期时间为millisecond毫秒 NX只在键不存在时才对键进行设置操作 XX只在键已经存在时才对键进行设置操作 SET操作完成时返回OK否则返回nil。 2.del导致误删 假设在这样一个场景当中假如某线程成功获得了锁并且设置的超时时间是30秒。
但是如果由于某些原因导致线程A执行的速度很慢过了30s都还没执行完成这时候锁过期自动释放线程B得到了锁。
随后线程A执行完了任务线程A接着执行del指令来释放锁但这时候线程B还没执行线程A实际上删除的是线程B加的锁
解决办法
在del释放锁之前加一个判断验证当前的锁是不是自己加的锁。
具体在加锁的时候把当前线程的id当做value在删除之前验证key对应的value是不是自己线程的id
3.出现并发的可能性 第三个问题其实还是刚才第二点所描述的场景。虽然避免了线程A误删掉key的情况但是同一时间有A和B两个线程在访问代码块还是不完美的。
解决办法
可以让获得锁的线程开启一个守护线程用来给快要过期的锁”续航“。
当过去了29秒线程A还没执行完这时候守护线程会执行expire指令为这把锁“续命”20秒。守护线程从第29秒开始执行每20秒执行一次。当线程A执行完任务会显式关掉守护线程。
另一种情况如果节点1 忽然断电由于线程A和守护线程在同一个进程守护线程也会停下。这把锁到了超时的时候没人给它续命也就自动释放了。