聊聊Mybatis的缓存之装饰者模式

举报
周杰伦本人 发表于 2022/08/30 11:10:26 2022/08/30
【摘要】 聊聊Mybatis的缓存之装饰者模式 装饰器的Component被装饰者Cache接口是装饰器的Component被装饰者public interface Cache { String getId(); void putObject(Object key, Object value); Object getObject(Object key); Object remov...

聊聊Mybatis的缓存之装饰者模式

装饰器的Component被装饰者

Cache接口是装饰器的Component被装饰者

public interface Cache {
  String getId();
  
  void putObject(Object key, Object value);
  
  Object getObject(Object key);
  
  Object removeObject(Object key);
  
  void clear();
  
  int getSize();
  
  default ReadWriteLock getReadWriteLock() {
    return null;
  }

}

接口中方法有放入对象、获取对象和移除对象的方法,还有一个方法是获取读写锁的方法。

PerpetualCache类实现了这个接口,它使用的是HashMap集合来缓存数据

Cache是装饰器模式的Component接口,PerpetualCache是接口的实现类

装饰器模式的装饰者

BlockingCache实现了Cache接口,具有阻塞线程的功能,看一下它的获取缓存数据的方法

获取数据

BlockingCache的getObject()方法

    @Override
    public Object getObject(Object key) {
        acquireLock(key);
        Object value = delegate.getObject(key);
        if (value != null) {
            releaseLock(key);
        }
        return value;
    }
  1. 先调用acquireLock()方法获取key对应的锁
  2. 调用被装饰者的getObject()方法
  3. 当获取到的值不为空的时候释放锁,为空就不释放锁了
  4. 返回key对应的value值

获取锁

获取锁BlockingCache的acquireLock()方法:

  private final ConcurrentHashMap<Object, CountDownLatch> locks;
  private void acquireLock(Object key) {
    CountDownLatch newLatch = new CountDownLatch(1);
    while (true) {
      CountDownLatch latch = locks.putIfAbsent(key, newLatch);
      if (latch == null) {
        break;
      }
      try {
        if (timeout > 0) {
          boolean acquired = latch.await(timeout, TimeUnit.MILLISECONDS);
          if (!acquired) {
            throw new CacheException(
                "Couldn't get a lock in " + timeout + " for the key " + key + " at the cache " + delegate.getId());
          }
        } else {
          latch.await();
        }
      } catch (InterruptedException e) {
        throw new CacheException("Got interrupted while trying to acquire lock for key " + key, e);
      }
    }
  }
  1. 创建CountDownLatch对象,key和CountDownLatch对象进行关联,ConcurrentHashMap用来保存key和CountDownLatch的关联
  2. 如果CountDownLatch对象为空,说明ConcurrentHashMap没有key对应的CountDownLatch对象,就没有竞争,锁获取成功
  3. 如果CountDownLatch对象不为空,说明发生了竞争,就阻塞当前线程,直到latch.countDown()方法来进行唤醒

释放锁

看下releaseLock()方法:

private void releaseLock(Object key) {
        CountDownLatch latch = locks.remove(key);
        if (latch == null) {
            throw new IllegalStateException("Detected an attempt at releasing unacquired lock. This should never happen.");
        }
        latch.countDown();
    }
  1. 删除ConcurrentHashMap中key和CountDownLatch对象的关联
  2. 唤醒等待的线程

在真正使用的时候,线程之间产生竞争,线程a发现key没有关联的CountDownLatch对象,获取锁成功,获取锁成功后在ConcurrentHashMap集合中维护key和CountDownLatch对象的关联,线程b进入是没办法获取锁的,产生阻塞,这时候需要查询数据库的数据并调用putObject()方法来释放锁,从而线程b被唤醒

总结

本篇文章主要从装饰者模式的角度分析了一下缓存模块的被装饰器接口Cache和实现类PerpetualCache,PerpetualCache通过HasnMap来缓存数据,还介绍了一个装饰器BlockingCache,分析了它获取缓存的逻辑:先获取key对应的锁然后获取数据最后释放锁,CountDownLatch中保存key和CountDownLatch对象的关联来表示加锁是否成功

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。