理论:http://blog.csdn.net/ugg/article/details/41894947
代码:http://blog.csdn.net/he90227/article/details/69568702
个人理解:
redis分布式锁主要是从判断(key,value)是否存在来断定是否让线程获取到锁,如果获得锁,执行业务,未获取到锁,不执行业务,阻止并发线程出错。
看自己写的伪代码:
public void lock(String lockKey, String currentThreadTime){
boolean lock = false;//初始化未获取到锁
boolean success = SETNX(lockKey, currentThreadTime);
if(success){
//通过命令setnx即获取到了锁(这个资源锁被释放,或者被redis因过期del)
expire(currentThreadTime);//设置过期时间,防止因当前线程获取锁后,突然挂掉而导致锁不被释放
lock = true;//代表获取到锁
}else{
//说明该资源被其他线程占了锁
valueTimeStr = GET(lockKey);//获取到锁的时间戳值
if(valueTimeStr != null && valueTimeStr < systemCurrentTime){
//该锁未被其他线程del且过期----A
oldValueTimeStr = GETSET(lockKey,currentThreadTime);
//当前线程发现锁过期,决定尝试获取用getset获取锁
if(oldValueTimeStr != null && valueTimeStr.equals(oldValueTimeStr)){
//getset命令去获取旧的时间,看从A进来这段代码期间,有没有其他线程也发现过期而del或者修改掉了锁的值
lock = true;//获得锁
}else{
//虽然GET的时候发现锁过期了,但是在准备获取这个锁时,其他线程抢先del或者修改了lockKey的值
sleep(200);//线程死等200毫秒,再看下一次循环
}
}else{
//因过期redis服务器删了锁,或者其他线程发现锁过期删了锁
sleep(200);//等待下一次循环
}
}
}
总结:1、获得锁的两个状态:
a.(lockKey,value)这对键值对未创建,也就是说没有其他线程正在占用该锁时
b. lockKey被占用,但是过期了,且未被其他线程抢先占有,代码片段如下:
oldValueTimeStr != null && valueTimeStr.equals(oldValueTimeStr)
2、锁设置过期时间是为了防止死锁,假设C1线程SETNX成功了,但是突然挂掉了,一直不释放锁,其他线程只能干等,造成死锁
3、使用到的三个命令:
SETNX,GET,GETSET,DEL
4、关于GETSET,如果有其他线程获取了锁,设置了新的过期时间,那么当前线程GETSET并不会获取锁,此时又修改了时间,会不会导致过期时间一直增长,导致锁永远不过期,答案是不会,当两个线程在getset这里进行竞争时,说明两个线程设置的时间差也很小,不会影响业务
问题思考:
1、过期时间设置多长合适,如果业务时间大于锁过期时间,线程C1获取锁后执行业务时,线程C2发现锁过期,然后通过GETSET获取了锁,进而执行业务,那么此时,C1,C2同时执行业务,是不是造成了并发的情况
2、SETNX失败是否直接让线程等待锁过期,被服务器删除后尝试,而不用进行GETSET后面的操作,直接等待
分析:
问题1:如果业务的执行时间大于锁过期时间,说明锁过期时间设置过短,需要设置更加合理的过期时间,纯属于业务时间评估问题
问题2:不可行,过期键删除默认是定期删除,懒删除两种,如果定期没有删除,轮询时间过长,那么线程会等待较长时间,懒删除,如果不去执行get,getset命令,其他线程不去访问,那就锁一直在
上面两个问题的关键就是,是否存在,线程1获取到锁,但是业务还没执行完,但是锁过期了?
再加一个流程图: