简单解锁 之 锁 的简单运用,单机锁 和 分布式锁
总结:锁分很多种,说起来都可怕,有些我自己都不是很清楚 具体可以参考 https://github.com/redisson/redisson/wiki/8.-%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81%E5%92%8C%E5%90%8C%E6%AD%A5%E5%99%A8
我这里主要讲一下
单机 : 常用的 锁 synchronized(一个对象持有 又可以 Class 持有)class 其实也是 Class 的一个对象 出异常主动释放 虚拟机自动
ReentrantLock 不会主动释放 需要自己手动释放 具体玩法自行百度 他们都是可重入(就是说同一个线程可以重复获取锁,内部维护了一个计数器)
多机:有基于 redis ,zk,mysql 等 实现的。
使用场景:公众号 access_token(一天调用次数有限,每次刷新的有效期2小时 老的token 还有5分钟的平滑期) 刷新 如果同时刷新 保存的又不是最新的 这样就会出问题
前后端分离(几秒内只允许用户操作一次,多次直接不予处理),防止 表单重复提交(在传统的web项目中,防止重复提交,通常做法是:进入保存页面,后端生成一个唯一的提交令牌(uuid),并存储在服务端。页面提交请求携带这个提交令牌,后端验证并在第一次验证后删除该令牌,保证提交请求的唯 一性,前端 disable ,当然还可以mysql 唯一索引键,直接捕获副本异常(不推荐))
话不多说redis 分布式锁如下:核心代码如下
@Around("@annotation(com.example.demo.annotation.NoRepeatSubmit)")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
MethodSignature signature = (MethodSignature) pjp.getSignature();
Method method = signature.getMethod();
NoRepeatSubmit noRepeatSubmit = method.getAnnotation(NoRepeatSubmit.class);
int lockSeconds = noRepeatSubmit.lockTime();
HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
Assert.notNull(request, "request can not null");
//此处可以用token或者JSessionId 还可以考虑 ip+请求参数+User-Agent 合起来md5
String token = request.getHeader("Authorization");
String path = request.getServletPath();
String key = getKey(token, path);
String clientId = getClientId();
boolean isSuccess = redisLock.tryLock(key, clientId, lockSeconds);
LOGGER.info("tryLock key = [{}], clientId = [{}]", key, clientId);
if (isSuccess) {
LOGGER.info("tryLock success, key = [{}], clientId = [{}]", key, clientId);
// 获取锁成功
Object result;
try {
// 执行进程
result = pjp.proceed();
} finally {
// 解锁
redisLock.releaseLock(key, clientId);
LOGGER.info("releaseLock success, key = [{}], clientId = [{}]", key, clientId);
}
return result;
} else {
// 获取锁失败,认为是重复提交的请求
LOGGER.info("tryLock fail, key = [{}]", key);
return new RepeatSubmitException("重复请求,请稍后再试");
}