创建UpdateLockTimeoutTask类来执行更新锁超时的时间。
public class UpdateLockTimeoutTask implements Runnable{ //uuid private long uuid; private StringRedisTemplate stringRedisTemplate; private String key; public UpdateLockTimeoutTask(long uuid, StringRedisTemplate stringRedisTemplate, String key){ this.uuid = uuid; this.stringRedisTemplate = stringRedisTemplate; this.key = key; } @Override public void run(){ //以uuid为key,当前线程id为value保存到Redis中 stringRedisTemplate.opsForValue().set(uuid, Thread.currentThread().getId()); //定义更新锁的过期时间 while(true){ springRedisTemplate.expire(key, 30, TimeUnit.SECONDS); try{ //每隔10秒执行一次 Thread.sleep(10000); }catch (InterruptedException e){ e.printStackTrace(); } } } }接下来,我们定义一个ThreadUtils工具类,这个工具类中有一个根据线程id获取线程的方法getThreadByThreadId(long threadId)。
public class ThreadUtils{ //根据线程id获取线程句柄 public static Thread getThreadByThreadId(long threadId){ ThreadGroup group = Thread.currentThread().getThreadGroup(); while(group != null){ Thread[] threads = new Thread[(int)(group.activeCount() * 1.2)]; int count = group.enumerate(threads, true); for(int i = 0; i < count; i++){ if(threadId == threads[i].getId()){ return threads[i]; } } } } }上述解决分布式锁失效的问题在分布式锁领域有一个专业的术语叫做 “异步续命” 。需要注意的是:当业务代码执行完毕后,我们需要停止更新锁超时时间的线程。所以,这里,我对程序的改动是比较大的,首先,将更新锁超时的时间任务重新定义为一个UpdateLockTimeoutTask类,并将uuid和StringRedisTemplate注入到任务类中,在执行定时更新锁超时时间时,首先将当前线程保存到Redis中,其中Key为传递进来的uuid。
在首先获取分布式锁后,重新启动线程,并将uuid和StringRedisTemplate传递到任务类中执行任务。当业务代码执行完毕后,调用releaseLock()方法释放锁时,我们会通过uuid从Redis中获取更新锁超时时间的线程id,并通过线程id获取到更新锁超时时间的线程,调用线程的interrupt()方法来中断线程。
此时,当分布式锁释放后,更新锁超时的线程就会由于线程中断而退出了。
实现分布式锁的基本要求结合上述的案例,我们可以得出实现分布式锁的基本要求:
支持互斥性
支持锁超时
支持阻塞和非阻塞特性
支持可重入性
支持高可用
通用分布式解决方案在互联网行业,分布式锁是一个绕不开的话题,同时,也有很多通用的分布式锁解决方案,其中,用的比较多的一种方案就是使用开源的Redisson框架来解决分布式锁问题。
有关Redisson分布式锁的使用方案大家可以参考《【高并发】你知道吗?大家都在使用Redisson实现分布式锁了!!》
既然Redisson框架已经很牛逼了,我们直接使用Redisson框架是否能够100%的保证分布式锁不出问题呢?答案是无法100%的保证。因为在分布式领域没有哪一家公司或者架构师能够保证100%的不出问题,就连阿里这样的大公司、阿里的首席架构师这样的技术大牛也不敢保证100%的不出问题。
在分布式领域,无法做到100%无故障,我们追求的是几个9的目标,例如99.999%无故障。
CAP理论在分布式领域,有一个非常重要的理论叫做CAP理论。
C:Consistency(一致性)
A:Availability(可用性)
P:Partition tolerance(分区容错性)
在分布式领域中,是必须要保证分区容错性的,也就是必须要保证“P”,所以,我们只能保证CP或者AP。
这里,我们可以使用Redis和Zookeeper来进行简单的对比,我们可以使用Redis实现AP架构的分布式锁,使用Zookeeper实现CP架构的分布式锁。
基于Redis的AP架构的分布式锁模型
在基于Redis实现的AP架构的分布式锁模型中,向Redis节点1写入数据后,会立即返回结果,之后在Redis中会以异步的方式来同步数据。
基于Zookeeper的CP架构的分布式锁模型