zl程序教程

您现在的位置是:首页 >  云平台

当前栏目

动态代理+读写锁实现线程安全的HashMap缓存工具类

代理工具安全缓存线程 实现 动态 读写
2023-06-13 09:13:00 时间

背景

jdk1.8之前是没有线程安全的集合工具类,例如currentHashMap,那怎样实现高效、线程安全的集合工具类呢? 可以利用读写锁实现线程安全,动态代理帮助集合作为工具类,产生更多的使用场景,例如缓存

代码

1. 创建缓存基类和子类

基类里的读写锁

private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public void readLock() {
    try {
        readWriteLock.readLock().lock();
    } catch (Exception e) {
        throw new CacheRuntimeException(e);
    }
}

public void releaseRead(){
    readWriteLock.readLock().unlock();
}

public void writeLock() {
    try {
        readWriteLock.writeLock().lock();
    } catch (Exception e) {
        throw new CacheRuntimeException(e);
    }
}

public void releaseWrite(){
    readWriteLock.writeLock().unlock();
}

子类里的基本方法

private HashBiMap<Integer,String> idAndNameBiMap;

public void init() {
    idAndNameBiMap= HashBiMap.create();
    System.out.println("idAndNameBiMap init");
}

public Integer getIdByName(String name){
    System.out.println("getIdByName");
    return idAndNameBiMap.inverse().get(name);
}

public String getNameById(Integer id){
    System.out.println("getNameById");
    return idAndNameBiMap.get(id);
}

public void putIdAndName(Integer id,String name){
    System.out.println("putIdAndName");
    idAndNameBiMap.put(id, name);
}

2. 动态代理实现子类的增强

1. 创建代理类

public synchronized AbstractCacheImpl getTarget() {
    if (targetCrated) {
        return target;
    }
    if (ClassUtils.isAssignable(this.cacheImpl, AbstractCacheImpl.class)) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.cacheImpl);
        enhancer.setCallback(this);
        target = (AbstractCacheImpl) enhancer.create();
        target.setCallbackId(getProxyName());
        if (MapUtil.isNotEmpty(cacheProperties)) {
            BeanUtil.fillBeanWithMap(cacheProperties, target, false);
        }
    } else {
        throw new CacheRuntimeException(
                "cacheImpl isn't a subclass of AbstractCacheImpl" + this);
    }
    initCache();
    targetCrated = true;
    return target;
}

2. 方法拦截器

@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    try {
        AbstractCacheImpl cache = (AbstractCacheImpl) o;
        Object retFromSuper = null;
        if (ignoreMethods.contains(method.getName())) {
            retFromSuper = methodProxy.invokeSuper(cache, objects);
        } else if (writeMethod.contains(method.getName())) {
            System.out.println("writeLock:"+method.getName());
            cache.writeLock();
            try {
                retFromSuper = methodProxy.invokeSuper(cache, objects);
            } finally {
                cache.releaseWrite();
                System.out.println("releaseWrite");
            }
        } else if (readMethod.contains(method.getName())) {
            System.out.println("readLock:"+method.getName());
            cache.readLock();
            try {
                retFromSuper = methodProxy.invokeSuper(cache, objects);
            } finally {
                cache.releaseRead();
                System.out.println("releaseRead");
            }
        } else {
            retFromSuper = methodProxy.invokeSuper(cache, objects);
        }
        return retFromSuper;
    } catch (Exception e) {
        throw new RuntimeException(e).fillInStackTrace();
    }
}

3. 验证

1. 将动态代理类注入IOC容器

@Configuration
public class UserCacheConfigration {

@Bean("userCacheBean")
public CacheProxy userCacheBean() {
    return CacheProxy.builder()
            .proxyName("userCache")
            // 缓存子类的初始化方法
            .initMethod("init")
            // 缓存子类
            .cacheImpl(UserCache.class)
            //  需要上读锁的方法
            .readMethod(Sets.newHashSet("getNameById"))
            // 需要上写锁的方法
            .writeMethod(Sets.newHashSet("putIdAndName"))
            .build();
}
}

2. 单元测试

@Test
public void testUserCache(){
    UserCache target = (UserCache) cacheProxy.getTarget();
    target.putIdAndName(1,"张三");
    target.putIdAndName(2,"李四");
    System.out.println(target.getIdByName("张三"));
    System.out.println(target.getNameById(2));
}