0
点赞
收藏
分享

微信扫一扫

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

@[TOC]

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

装饰器的Component被装饰者

Cache接口:

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接口,具有阻塞线程的功能,看一下它的获取缓存数据的方法:

    @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对象的关联来表示加锁是否成功

举报

相关推荐

0 条评论