Mybatis缓存模块(一)BlockingCache

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

MyBatis作为一个强大的持久层框架,缓存是其必不可少的功能之一,MyBatis中的缓存是两层结构的,分为一级缓存,二级缓存,但本质上市相同的,它们使用的都是Cache接口的实现。

 一 Cache 接口及其实现

 

public interface Cache {

  // 该缓存对象的Id
  String getId();

  //向缓存中添加数据
  void putObject(Object key, Object value);

 //根据key 在缓存中查找结果
  Object getObject(Object key);

  // 删除key 对应的缓存项
  Object removeObject(Object key);

  // 清空缓存
  void clear();

  //缓存项个数
  int getSize();
  
  // 获取读写锁
  ReadWriteLock getReadWriteLock();

}

Cache 接口 有多个实现。在Idea 中Ctrl+Alt+shift+U可以查看类图

Mybatis缓存模块(一)BlockingCache_第1张图片

这些类中大部分都是装饰器(装饰器模式),只有PrepetualCache提供了Cache接口的基本实现。PrepetualCache在缓存中扮演着ConcreteComponent的角色,其底层实现比较简单,使用HashMap 记录缓存项。代码如下

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;

import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.cache.CacheException;

/**
 * @author Clinton Begin
 */
public class PerpetualCache implements Cache {
  
  // 缓存对象的唯一标识
  private String id;
  //记录缓存项
  private Map cache = new HashMap();

  public PerpetualCache(String id) {
    this.id = id;
  }

  @Override
  public String getId() {
    return id;
  }
  
  // map的大小
  @Override
  public int getSize() {
    return cache.size();
  }
  // hashMap 中添加缓存对象
  @Override
  public void putObject(Object key, Object value) {
    cache.put(key, value);
  }
  // hashMap 中查找缓存对象
  @Override
  public Object getObject(Object key) {
    return cache.get(key);
  }
  // hashMap 中删除对象
  @Override
  public Object removeObject(Object key) {
    return cache.remove(key);
  }
  // 清空hashMap
  @Override
  public void clear() {
    cache.clear();
  }
  //装饰着中有具体实现
  @Override
  public ReadWriteLock getReadWriteLock() {
    return null;
  }

  @Override
  public boolean equals(Object o) {
    if (getId() == null) {
      throw new CacheException("Cache instances require an ID.");
    }
    if (this == o) {
      return true;
    }
    if (!(o instanceof Cache)) {
      return false;
    }

    Cache otherCache = (Cache) o;
    return getId().equals(otherCache.getId());
  }

  @Override
  public int hashCode() {
    if (getId() == null) {
      throw new CacheException("Cache instances require an ID.");
    }
    return getId().hashCode();
  }

}

二 装饰器(BlockingCache

 下面介绍一下cache.decorators 包下的装饰器,他们都直接实现了cache接口,扮演者ConcreteDecorator的角色。这些装饰器会在PerpetualCache的基础上提供一下额外的功能,通过租后满足一定的需求。

 1  BlockingCache 的get

   BlockingCache 是阻塞版本的缓存装饰器,它保证只有一个线程到数据库中查找指定key对应的数据。

  加入线程A 在BlockingCache 中未查找到keyA对应的缓存项时,线程A会获取keyA对应的锁,这样后续线程在查找keyA是会发生阻塞,如下图所示

  

Mybatis缓存模块(一)BlockingCache_第2张图片

代码实现如下

@Override
public Object getObject(Object key) {
  // 获取key对应的锁
  acquireLock(key);
  // 查询key
  Object value = delegate.getObject(key);
  if (value != null) {
    // 如果从缓存(PrepetualCache是用HashMap实现的)中查找到,则释放锁,否则继续持有锁
    releaseLock(key);
  }        
  return value;
}

 

acquireLock() 方法会尝试获取指定的可以对应的锁。如果该key没有对应的锁对象则为其创建新的ReentrantLock 对象,再加锁;如果获取失败,则阻塞一段时间。

 

private void acquireLock(Object key) {
   // 获取ReentrantLock 对象
  Lock lock = getLockForKey(key);
  if (timeout > 0) {
    try {
     // 指定的时间内是否能够获取锁
      boolean acquired = lock.tryLock(timeout, TimeUnit.MILLISECONDS);
      // 超时抛出异常
      if (!acquired) {
        throw new CacheException("Couldn't get a lock in " + timeout + " for the key " +  key + " at the cache " + delegate.getId());  
      }
    } catch (InterruptedException e) {
      throw new CacheException("Got interrupted while trying to acquire lock for key " + key, e);
    }
  } else {
    // 如果timeout<=0 既没有时间设置,直接获取锁
    lock.lock();
  }
}
private ReentrantLock getLockForKey(Object key) {
 // 创建ReentrantLock对象
  ReentrantLock lock = new ReentrantLock();
  /**
   *private final ConcurrentHashMap locks;
   * 尝试添加到locks集合中,如果locks集合中已经有了相应的Reentrantock对象,则使用原有的locks 中的ReentrantLock对象
  **/
  ReentrantLock previous = locks.putIfAbsent(key, lock);
  return previous == null ? lock : previous;
}

 1  BlockingCache 的put

   假如线程A从数据库中查找到keyA对应的结果对象后,将结果放入到BlockingCache 中,此时线程A会释放keyA对应的锁,唤醒阻塞在该锁上的线程,其它线程可以从缓存中获取数据,而不是再次访问数据库。

 

Mybatis缓存模块(一)BlockingCache_第3张图片

@Override
public void putObject(Object key, Object value) {
  try {
    // 向缓存中添加缓存页
    delegate.putObject(key, value);
  } finally {
    // 释放锁
    releaseLock(key);
  }
}
private void releaseLock(Object key) {
  ReentrantLock lock = locks.get(key);
  // 锁是否被当前线程持有
  if (lock.isHeldByCurrentThread()) {
   // 释放所
    lock.unlock();
  }
}

转载于:https://my.oschina.net/u/3218528/blog/1614092

你可能感兴趣的:(Mybatis缓存模块(一)BlockingCache)