天天看點

動态代理+讀寫鎖實作線程安全的HashMap緩存工具類

背景

jdk1.8之前是沒有線程安全的集合工具類,例如

currentHashMap

,那怎樣實作高效、線程安全的集合工具類呢?

可以利用讀寫鎖實作線程安全,動态代理幫助集合作為工具類,産生更多的使用場景,例如緩存

代碼

1. 建立緩存基類和子類

動态代理+讀寫鎖實作線程安全的HashMap緩存工具類

基類裡的讀寫鎖

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. 動态代理實作子類的增強

動态代理+讀寫鎖實作線程安全的HashMap緩存工具類

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

複制

動态代理+讀寫鎖實作線程安全的HashMap緩存工具類