我们设置key的时候,将value设置为一个随机值r,并且存在当前线程ThreadLocal。当释放锁,也就是删除key的时候,不是直接删除,而是先判断该key对应的value是否等于先前存在当前线程的随机值,只有当前当前线程持有锁,才删除该key,由于每个客户端产生的随机值是不一样的,这样一来就不会误释放别的客户端申请的锁了
代码语言:txt复制public class RedisLock {代码语言:txt复制 private static Logger logger = LoggerFactory.getLogger(RedisLock.class);代码语言:txt复制 private static OnecachePlugin oneCache;代码语言:txt复制 static {代码语言:txt复制 oneCache = SpringUtil.getBean(OnecachePlugin.class);代码语言:txt复制 }代码语言:txt复制 /**代码语言:txt复制 * 基础有效时间
*/
private static final int BASE_VAILD_TIME = 10;代码语言:txt复制 /**代码语言:txt复制 * 锁的基本等待时间10s
*/
private static final int BASE_WAIT_TIME = 4;代码语言:txt复制 /**代码语言:txt复制 * 随机数
*/
private static Random random = new Random();代码语言:txt复制 private static ThreadLocal<Set<String>> threadLocal = new ThreadLocal<>();代码语言:txt复制 private static void currentThreadSleep() throws InterruptedException {代码语言:txt复制 Thread.sleep((long) (200), random.nextInt(5000));代码语言:txt复制 }代码语言:txt复制 public static boolean easyLock(String key) throws Exception {代码语言:txt复制 return easyLock(key, BASE_WAIT_TIME, BASE_VAILD_TIME);代码语言:txt复制 }代码语言:txt复制 //加锁代码语言:txt复制 public static boolean easyLock(String key, Integer waitTime, Integer expireTime) throws Exception {代码语言:txt复制 if (ObjectUtil.hasEmpty(waitTime)) {代码语言:txt复制 waitTime = BASE_WAIT_TIME;代码语言:txt复制 }代码语言:txt复制 if (ObjectUtil.hasEmpty(expireTime)) {代码语言:txt复制 expireTime = BASE_VAILD_TIME;代码语言:txt复制 }代码语言:txt复制 Long signTime = System.nanoTime();代码语言:txt复制 Long holdTime = TimeUnit.NANOSECONDS.convert(waitTime, TimeUnit.SECONDS);代码语言:txt复制 String state = UUIDGenerator.getUUID();代码语言:txt复制 while ((System.nanoTime() - signTime) < holdTime) {代码语言:txt复制 //从redis获取key 如果不存在,则将key存入redis代码语言:txt复制 RString rs = oneCache.getRString(key);代码语言:txt复制 //原子性加锁代码语言:txt复制 if (rs.setnx(state, Time.seconds(expireTime))) {代码语言:txt复制 logger.info(Thread.currentThread().getId() " 获取锁成功! key = " key);代码语言:txt复制 if(ObjectUtil.hasEmpty(threadLocal.get())){代码语言:txt复制 threadLocal.set(Sets.newHashSet());代码语言:txt复制 }代码语言:txt复制 threadLocal.get().add(state);代码语言:txt复制 return true;代码语言:txt复制 } else {代码语言:txt复制 try {代码语言:txt复制 currentThreadSleep();代码语言:txt复制 } catch (InterruptedException e) {代码语言:txt复制 Thread.currentThread().interrupt();代码语言:txt复制 }代码语言:txt复制 }代码语言:txt复制 }代码语言:txt复制 throw new Exception( "系统正忙,稍后重试");代码语言:txt复制 }代码语言:txt复制 /**代码语言:txt复制 * redis简单分布式锁,业务执行完毕之后必须try finally 调用释放锁方法easyUnLock
*
* @param key 锁
*/
public static void easyUnLock(String key) {
logger.info(Thread.currentThread().getId() " 准备解锁! key = " key);代码语言:txt复制 //是否是当前线程持有锁,以免释放其他线程加的锁代码语言:txt复制 if (isHoldEasyLock(key)) {代码语言:txt复制 logger.info(Thread.currentThread().getId() " 开始解锁! key = " key);代码语言:txt复制 try {代码语言:txt复制 //从redis删除key代码语言:txt复制 RString rs = oneCache.getRString(key);代码语言:txt复制 String state = rs.get();代码语言:txt复制 rs.delete();代码语言:txt复制 threadLocal.get().remove(state);代码语言:txt复制 logger.info(Thread.currentThread().getId() " 解锁成功! key = " key);代码语言:txt复制 } catch (Exception e) {代码语言:txt复制 logger.info("RedisLockUtils easyLock解锁异常 ->" e);代码语言:txt复制 }代码语言:txt复制 }代码语言:txt复制 }代码语言:txt复制 /**代码语言:txt复制 * redis简单分布式锁,判断线程是否持有锁
*/
public static boolean isHoldEasyLock(String key) {
if (ObjectUtil.hasEmpty(threadLocal.get())) {
return false;
}
if (threadLocal.get().contains(oneCache.getRString(key).get())) {
return true;
} else {
return false;
}
}代码语言:txt复制}实际上,这样还是有一点问题,释放锁不是原子性,很有可能在查询完,redis也刚过期,再删除就把别的线程的锁释放了。


