动物园管理员(3)分布式锁

  

在一个分布式系统中,如何保证一个操作,同一时间只有一个线程可以执行,这就是分布式锁的使用场景,同一时间,只有一个线程可以获得锁的使用权。

  

如何实现一个分布式锁吗?

  

实现一个分布式锁,可以有以下3种方法。

  

一、基于数据库实现分布式锁。

  

1,在MySQL中,使用悲观锁“从t id=选择 update"可以对行数据进行加锁,来实现分布式锁。
2,同一个时间内,只会有一个线程加锁成功,其他线程必须等待。
3,数据库要保证是全局的,每一把锁所对应的行数据也是唯一的。

  

优点:

  

1,实现简单,方便。

  

缺点:

  

1,基于数据库的悲观锁,性能比较差。
2,等待中的线程是自旋的或者等待状态,需要等待持有锁的线程处理完,多个线程再一起去竞争同一把锁。
3,异常无法处理,当持有锁的线程还没有释放锁,意外退出,锁资源将无法释放,应用程序将无法继续进行。

  

二,基于复述,实现的分布式锁。

  

1,使用复述的setnx命令,可以模拟分布式锁,setnx保证操作一个关键值,如果没有则返回真,如果存在则返回假。
2,为了解决程序意外退出导致无法释放锁资源,需要给关键增加一个超时时间。
3,复述,提供了带参数的命令,可以保证设置关键值和设置超时时间这2个操作的原子性

  
 <代码>设置键值[秒]交货(PX毫秒)(NX | XX)  
  

4,假如有如下情况:

  

获取了锁,设置了超时时间为10秒,但是一个执行了15秒,在10秒的时候锁失效了。
B在11秒的时候获取了锁,执行了5秒,那么在15秒的时候会误删掉B的锁。

  

为了解决以上问题,每个加锁的线程都需要设置自己的价值值,当删除的时候也要校验是自己的锁,才可以删除。

  

优点:

  

1,实现简单,方便。
2,基于复述的高性能,效率高。

  

缺点:

  

1,无法解决超时锁失效问题,逻辑处理的时间超过了设置的超时时间,那么这个时候就会导致,另一个也可以拿到锁继续执行。

  

为了解决这个问题,一般的方法是启动一个守护线程,时刻监控失效时间,当锁时间超过一定的执行时间比例之后,自动续约一定的时间,当然这个总时间是有最大阈值限制的。

  

2,等待中的线程是自旋的后者等待状态,需要等待持有锁的线程处理完,多个线程再一起去竞争同一把锁。

  

三,饲养员实现分布式锁

  

方式1:悲观锁

  

1,使用临时节点实现分布式锁,第一个创建临时节点成功的线程,获取锁成功。
2,其他创建临时节点的线程会失败,那么监听锁的临时节点。
3,当锁释放的时候,删除临时节点,会通知到监听的线程,收到通知的线程继续尝试创建临时锁节点,谁创建成功谁获得锁。

  

优点:

  

解决了锁失效问题,通知机制可以完美解决,即使创建临时节点的线程挂掉,临时节点会自动删除。

  

缺点:

  

1,当有大量线程,等待锁资源的时候,锁资源释放会涉及到大量的通知,并且大量的线程需要一起竞争锁资源。

  

方式2:乐观锁

  

1,在一个目录下,各个线程创建顺序的临时节点,节点编号1,2,3,4,5等。
2,目录下创建的节点最小的线程获取锁。
3,等待锁资源的线程,不再一起全部监听锁节点,而是只监听比自己小的上一个节点。
4,当监听的比自己小的锁节点被删除后,继续改为监听上一个比自己小的节点。
4,当锁释放的时候,只需要通知监听锁节点的一个获几个线程,避免了大量的通知。

  

管理员实现的乐观锁,是比较合理的分布式锁方式,感兴趣的朋友可以用代码实现一下。

动物园管理员(3)分布式锁