读写锁注解解决修改数据库和删除缓存的短暂时间内的数据不一致问题

先定义一个枚举。

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();
            }

        }

    }

}

遇到一个面试题。修改数据库然后删除缓存的短暂时间内的数据不一致问题怎么解决?

我想用读写锁来进行控制。读缓存的时候,使用读锁或不使用锁。修改数据库&删除缓存的时候,创建读写锁,使用写锁,此时发生的读操作,就会使用读锁。

如此就能解决了修改数据和读取数据的数据不一致问题。

哪里考虑不周,欢迎各位大佬指正!

猜你喜欢

转载自www.cnblogs.com/itbac/p/11537811.html