先定义一个枚举。
package com.study.security.common.annotation;
/**
* @Description: 读、写锁 类型
* @Auther: BacHe
* @Date: 2019/9/17 09:39
*/
public enum ReadWriteType {
//读锁
READ_TYPE,
//写锁
WRITE_TYPE
}
自定义一个注解
package com.study.security.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Description: 读、写锁 注解
* @Auther: BacHe
* @Date: 2019/9/17 09:41
*/
//注解生命周期:运行时有有效
@Retention(RetentionPolicy.RUNTIME)
//注解应用范围:修饰方法
@Target({ElementType.METHOD})
public @interface ReadWriteLock {
String key() default "ReadWriteLock_";
//默认是:读锁。
ReadWriteType type() default ReadWriteType.READ_TYPE;
}
写获取读写锁的工具类,根据key 不同,使用不同的读写锁。
package com.study.security.common.annotation;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* @Description: 读写锁工具
* @Auther: BacHe
* @Date: 2019/9/17 10:02
*/
public class ReadWriteLockUtil {
private static Map<String,ReentrantReadWriteLock> map = new ConcurrentHashMap();
//获取锁,并保存到map中
public static ReentrantReadWriteLock getLock(String key){
ReentrantReadWriteLock readWriteLock =map.get(key);
if (null == readWriteLock) {
synchronized (key) {
readWriteLock = map.get(key);
if (null == readWriteLock) {
readWriteLock = new ReentrantReadWriteLock();
map.put(key, readWriteLock);
}
}
}
return readWriteLock;
}
//获取锁,可能拿不到锁
public static ReentrantReadWriteLock getLockOrNull(String key){
return map.get(key);
}
//从map 中删除锁。
public static void delLock(String key) {
map.remove(key);
}
}
对注解进行AOP切面编程
package com.study.security.common.annotation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* @Description: 读写锁 AOP 切面编程
* @Auther: BacHe
* @Date: 2019/9/17 09:49
*/
@Component
@Aspect
public class ReadWriteLockAop {
@Around("@annotation(com.study.security.common.annotation.ReadWriteLock)")
public void lock(ProceedingJoinPoint joinPoint) throws NoSuchMethodException {
String key = "lock_";
//获取方法签名
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
//从切入点,获取目标对象的字节码,的方法。参数:(方法名,方法的所有参数类型)
Method method = joinPoint.getTarget().getClass().getMethod(signature.getName(), signature.getMethod().getParameterTypes());
//从方法获取注解。
ReadWriteLock annotation = method.getAnnotation(ReadWriteLock.class);
//从注解,获取注解信息。'ReadWriteLockAop' + #key
String keyEL = annotation.key();
ReadWriteType type = annotation.type();
//2. 创建 springEL表达式 解析器
SpelExpressionParser parser = new SpelExpressionParser();
// 解析器 获取指定表达式 'ReadWriteLockAop' + #key 的表达式对象
Expression expression = parser.parseExpression(keyEL);
// 设置解析上下文
StandardEvaluationContext context = new StandardEvaluationContext();
//2.1 创建默认参数名 发现者
DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
//2.2 获取方法中的所有参数名。
String[] parameterNames = discoverer.getParameterNames(method);
//2.3 获取切点方法中的所有参数值。
Object[] args = joinPoint.getArgs();
for (int i = 0; i < parameterNames.length; i++) {
// 把参数名,参数值,设置到解析器上下文
context.setVariable(parameterNames[i],args[i].toString());
}
//表达式 匹配 解析上下文 中的内容 ,拿到key
key = key + expression.getValue(context).toString();
if (type == ReadWriteType.READ_TYPE) {
ReentrantReadWriteLock lock = ReadWriteLockUtil.getLockOrNull(key);
//读锁。可能拿到null
if (null != lock) {
lock.readLock().lock();
}
try {
//执行切点方法。
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
} finally {
if (null != lock) {
//释放锁
lock.readLock().unlock();
}
}
} else {
//写锁。一定能拿到锁,非null
ReentrantReadWriteLock lock = ReadWriteLockUtil.getLock(key);
lock.writeLock().lock();
try {
//执行切点方法。
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
} finally {
//删除锁
ReadWriteLockUtil.delLock(key);
//释放锁
lock.writeLock().unlock();
}
}
}
}
遇到一个面试题。修改数据库然后删除缓存的短暂时间内的数据不一致问题怎么解决?
我想用读写锁来进行控制。读缓存的时候,使用读锁或不使用锁。修改数据库&删除缓存的时候,创建读写锁,使用写锁,此时发生的读操作,就会使用读锁。
如此就能解决了修改数据和读取数据的数据不一致问题。
哪里考虑不周,欢迎各位大佬指正!