博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Mybatis缓存模块(一)BlockingCache
阅读量:5975 次
发布时间:2019-06-19

本文共 4190 字,大约阅读时间需要 13 分钟。

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可以查看类图

105154_2wtR_3218528.png

这些类中大部分都是装饰器(),只有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是会发生阻塞,如下图所示

  

112142_j5Vf_3218528.png

代码实现如下

@Overridepublic 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对应的锁,唤醒阻塞在该锁上的线程,其它线程可以从缓存中获取数据,而不是再次访问数据库。

 

140246_wv1M_3218528.png

@Overridepublic 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

你可能感兴趣的文章
【java】 linux下利用nohup后台运行jar文件包程序
查看>>
Java Lambda表达式初探
查看>>
前端进阶之路:初涉Less
查看>>
Android Fragment使用(三) Activity, Fragment, WebView的状态保存和恢复
查看>>
【CLR via C#】CSC将源代码编译成托管模块
查看>>
Python协程:从yield/send到async/await
查看>>
Python中的join()函数的用法
查看>>
C++ 用RGB 三种颜色绘图
查看>>
EntityFramework Core 1.1是如何创建DbContext实例的呢?
查看>>
JDK1.7安装配置环境变量+图文说明Jmeter安装
查看>>
「代码家」的学习过程和学习经验分享【转】
查看>>
各种定律法则
查看>>
基本的输入输出函数介绍
查看>>
ASP.NET Core MVC/WebAPi如何构建路由?
查看>>
Swagger+AutoRest 生成web api客户端(.Net)
查看>>
Sublime Text 3插件收集
查看>>
Spring bean加载多个配置文件
查看>>
[Spring MVC] - @ModelAttribute使用
查看>>
【java设计模式】之 代理(Proxy)模式
查看>>
微信小程序价值思考:手机端的CS-BS迁移
查看>>