zoukankan      html  css  js  c++  java
  • 读写锁注解解决修改数据库和删除缓存的短暂时间内的数据不一致问题

    先定义一个枚举。

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

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

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

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

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

    ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    有些大佬说,我这个是单机版的,不适合分布式项目。其实把存储的本地map换成存到redis缓存中,不就支持分布式了吗。

    package com.study.framework.annotation;
    
    import com.study.framework.cache.redis.RedisClient;
    
    import java.util.concurrent.locks.ReentrantReadWriteLock;
    
    /**
     * @Description: 读写锁工具
     * @Auther: BacHe
     * @Date: 2019/9/17 10:02
     */
    public class ReadWriteLockUtil {
    
    
        //获取锁,并保存到redis缓存中
        public static ReentrantReadWriteLock getLock(String key){
    
            ReentrantReadWriteLock readWriteLock = RedisClient.getValue(key, ReentrantReadWriteLock.class);
            if (null == readWriteLock) {
                synchronized (key) {
                    //双重检测
                    readWriteLock =  RedisClient.getValue(key, ReentrantReadWriteLock.class);
                    if (null == readWriteLock) {
                        readWriteLock = new ReentrantReadWriteLock();
                        RedisClient.setValue(key,readWriteLock);
                    }
                }
            }
            return readWriteLock;
        }
        //获取锁,可能拿不到锁
        public static ReentrantReadWriteLock getLockOrNull(String key){
            return   RedisClient.getValue(key, ReentrantReadWriteLock.class);
        }
        //从map 中删除锁。
        public static void delLock(String key) {
            RedisClient.delKey(key);
        }
    
    
    
    }

    还有什么问题,请指正!

  • 相关阅读:
    selenium基础(鼠标和键盘事件)
    Java:面向对象三大特征
    Java:面向对象(上)
    Java:数组
    Java:方法
    Java基础:程序结构控制
    Java基础:用户交互Scanner
    Java基础语法(下)
    Jenkins(Extended E-mail Notification)邮箱配置正确但是并没有发送邮件
    接口自动化测试与Jenkins集成(Freestyle project任务版)
  • 原文地址:https://www.cnblogs.com/itbac/p/11537811.html
Copyright © 2011-2022 走看看