LruCache
LruCache通过重写LinkedHashMap的removeEldestEntry方法移除最老的元素。
package org.apache.ibatis.cache.decorators;import java.util.LinkedHashMap;import java.util.Map;import java.util.concurrent.locks.ReadWriteLock;import org.apache.ibatis.cache.Cache;/*** Lru (least recently used) cache decorator** @author Clinton Begin*/public class LruCache implements Cache {private final Cache delegate;private Map<Object, Object> keyMap;private Object eldestKey;public LruCache(Cache delegate) {this.delegate = delegate;setSize(1024);}@Overridepublic String getId() {return delegate.getId();}@Overridepublic int getSize() {return delegate.getSize();}public void setSize(final int size) {keyMap = new LinkedHashMap<Object, Object>(size, .75F, true) {private static final long serialVersionUID = 4267176411845948333L;//重写LinkedHashMap的removeEldestEntry方法,//当数据量超过size时移除最老的元素@Overrideprotected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {boolean tooBig = size() > size;if (tooBig) {eldestKey = eldest.getKey();}return tooBig;}};}@Overridepublic void putObject(Object key, Object value) {delegate.putObject(key, value);//同时把key放到keyMap中,并判断是否删除缓存超出的数据cycleKeyList(key);}@Overridepublic Object getObject(Object key) {keyMap.get(key); //touchreturn delegate.getObject(key);}private void cycleKeyList(Object key) {keyMap.put(key, key);if (eldestKey != null) {//删除缓存delegate.removeObject(eldestKey);eldestKey = null;}}}
FifoCache
package org.apache.ibatis.cache.decorators;import java.util.Deque;import java.util.LinkedList;import java.util.concurrent.locks.ReadWriteLock;import org.apache.ibatis.cache.Cache;/*** FIFO (first in, first out) cache decorator** @author Clinton Begin*/public class FifoCache implements Cache {private final Cache delegate;//用队列保存缓存顺序private final Deque<Object> keyList;private int size;public FifoCache(Cache delegate) {this.delegate = delegate;this.keyList = new LinkedList<Object>();this.size = 1024;}@Overridepublic void putObject(Object key, Object value) {//把key保存到keyListcycleKeyList(key);delegate.putObject(key, value);}@Overridepublic void clear() {delegate.clear();keyList.clear();}private void cycleKeyList(Object key) {keyList.addLast(key);//缓存个数超出限制,删掉最新进入的if (keyList.size() > size) {Object oldestKey = keyList.removeFirst();delegate.removeObject(oldestKey);}}}
BlockingCache
当一个key的值为空的时候,阻塞线程直到key的值被塞进缓存才释放锁,这样也就避免了冲击数据库。
package org.apache.ibatis.cache.decorators;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReadWriteLock;import java.util.concurrent.locks.ReentrantLock;import org.apache.ibatis.cache.Cache;import org.apache.ibatis.cache.CacheException;/*** Simple blocking decorator** Simple and inefficient version of EhCache's BlockingCache decorator.* It sets a lock over a cache key when the element is not found in cache.* This way, other threads will wait until this element is filled instead of hitting the database.** @author Eduardo Macarron**/public class BlockingCache implements Cache {private long timeout;private final Cache delegate;//每个key都有一把锁private final ConcurrentHashMap<Object, ReentrantLock> locks;public BlockingCache(Cache delegate) {this.delegate = delegate;this.locks = new ConcurrentHashMap<Object, ReentrantLock>();}@Overridepublic void putObject(Object key, Object value) {try {delegate.putObject(key, value);} finally {//当key的值不为空就释放锁releaseLock(key);}}@Overridepublic Object getObject(Object key) {//先拿到锁acquireLock(key);Object value = delegate.getObject(key);if (value != null) {//如果值不为空就释放锁releaseLock(key);}return value;}@Overridepublic Object removeObject(Object key) {// despite of its name, this method is called only to release locksreleaseLock(key);return null;}//每个key生成一把锁private ReentrantLock getLockForKey(Object key) {ReentrantLock lock = new ReentrantLock();ReentrantLock previous = locks.putIfAbsent(key, lock);return previous == null ? lock : previous;}//如果key的值为空,那么第一个获取key的线程占据锁,//其他线程就会阻塞,当设置key不为空时就会释放锁,//然后其他线程拿到锁,就可以获取key的值了private void acquireLock(Object key) {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 {lock.lock();}}private void releaseLock(Object key) {ReentrantLock lock = locks.get(key);if (lock.isHeldByCurrentThread()) {lock.unlock();}}}
SynchronizedCache
SynchronizedCache在每个方法上都有synchronized同步标记,保证同一时刻只有一个线程操作缓存。
/*** Copyright 2009-2017 the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at** http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package org.apache.ibatis.cache.decorators;import java.util.concurrent.locks.ReadWriteLock;import org.apache.ibatis.cache.Cache;/*** @author Clinton Begin*/public class SynchronizedCache implements Cache {private final Cache delegate;public SynchronizedCache(Cache delegate) {this.delegate = delegate;}@Overridepublic synchronized void putObject(Object key, Object object) {delegate.putObject(key, object);}@Overridepublic synchronized Object getObject(Object key) {return delegate.getObject(key);}@Overridepublic synchronized Object removeObject(Object key) {return delegate.removeObject(key);}@Overridepublic synchronized void clear() {delegate.clear();}}
TransactionalCache
TransactionalCache是一个二级缓存事务缓冲区。此类保存会话期间要添加到二级缓存的所有缓存项。调用commit时将条目发送到缓存,如果回滚会话,则丢弃条目。
/*** Copyright 2009-2017 the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at** http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package org.apache.ibatis.cache.decorators;import java.util.HashMap;import java.util.HashSet;import java.util.Map;import java.util.Set;import java.util.concurrent.locks.ReadWriteLock;import org.apache.ibatis.cache.Cache;import org.apache.ibatis.logging.Log;import org.apache.ibatis.logging.LogFactory;/*** The 2nd level cache transactional buffer.** This class holds all cache entries that are to be added to the 2nd level cache during a Session.* Entries are sent to the cache when commit is called or discarded if the Session is rolled back.* Blocking cache support has been added. Therefore any get() that returns a cache miss* will be followed by a put() so any lock associated with the key can be released.** @author Clinton Begin* @author Eduardo Macarron*/public class TransactionalCache implements Cache {private static final Log log = LogFactory.getLog(TransactionalCache.class);private final Cache delegate;private boolean clearOnCommit;private final Map<Object, Object> entriesToAddOnCommit;private final Set<Object> entriesMissedInCache;public TransactionalCache(Cache delegate) {this.delegate = delegate;this.clearOnCommit = false;this.entriesToAddOnCommit = new HashMap<Object, Object>();this.entriesMissedInCache = new HashSet<Object>();}@Overridepublic String getId() {return delegate.getId();}@Overridepublic int getSize() {return delegate.getSize();}@Overridepublic Object getObject(Object key) {// issue #116Object object = delegate.getObject(key);if (object == null) {entriesMissedInCache.add(key);}// issue #146if (clearOnCommit) {return null;} else {return object;}}@Overridepublic ReadWriteLock getReadWriteLock() {return null;}@Overridepublic void putObject(Object key, Object object) {//先放在缓存区,提交事务时才会真正放到缓存entriesToAddOnCommit.put(key, object);}@Overridepublic Object removeObject(Object key) {return null;}//先清除缓冲区内容,提交事务时才会真正清除缓存@Overridepublic void clear() {clearOnCommit = true;entriesToAddOnCommit.clear();}//提交事务public void commit() {if (clearOnCommit) {delegate.clear();}flushPendingEntries();reset();}public void rollback() {unlockMissedEntries();reset();}private void reset() {clearOnCommit = false;entriesToAddOnCommit.clear();entriesMissedInCache.clear();}private void flushPendingEntries() {for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {delegate.putObject(entry.getKey(), entry.getValue());}for (Object entry : entriesMissedInCache) {if (!entriesToAddOnCommit.containsKey(entry)) {delegate.putObject(entry, null);}}}private void unlockMissedEntries() {for (Object entry : entriesMissedInCache) {try {delegate.removeObject(entry);} catch (Exception e) {log.warn("Unexpected exception while notifiying a rollback to the cache adapter."+ "Consider upgrading your cache adapter to the latest version. Cause: " + e);}}}}
LoggingCache
用于计算缓存命中率。
package org.apache.ibatis.cache.decorators;import java.util.concurrent.locks.ReadWriteLock;import org.apache.ibatis.cache.Cache;import org.apache.ibatis.logging.Log;import org.apache.ibatis.logging.LogFactory;/*** @author Clinton Begin*/public class LoggingCache implements Cache {private final Log log;private final Cache delegate;protected int requests = 0;protected int hits = 0;@Overridepublic Object getObject(Object key) {//总的请求数requests++;final Object value = delegate.getObject(key);if (value != null) {//缓存命中数hits++;}if (log.isDebugEnabled()) {//日志记录缓存命中率log.debug("Cache Hit Ratio [" + getId() + "]: " + getHitRatio());}return value;}private double getHitRatio() {return (double) hits / (double) requests;}}
装饰器模式
这些缓存通过装饰器组合,可以形成具有很多种功能的缓存。
PerpetualCache cache = new PerpetualCache("id");//具有lru功能LruCache lru = new LruCache(cache);//具有lru和log功能LoggingCache log = new LoggingCache(lru);//具有lru和log和同步锁功能SynchronizedCache sync = new SynchronizedCache(log);
