oscache源码浅析
oscache作为本地缓存框架,存储模型依然是通用的缓存键值对模型。oscache使用HashTable存放数据,我们看下源码:
GeneralCacheAdministrator:
/**
* Get an object from the cache
*
* @param key The key entered by the user.
* @param refreshPeriod How long the object can stay in cache in seconds. To
* allow the entry to stay in the cache indefinitely, supply a value of
* {@link CacheEntry#INDEFINITE_EXPIRY}
* @return The object from cache
* @throws NeedsRefreshException when no cache entry could be found with the
* supplied key, or when an entry was found but is considered out of date. If
* the cache entry is a new entry that is currently being constructed this method
* will block until the new entry becomes available. Similarly, it will block if
* a stale entry is currently being rebuilt by another thread and cache blocking is
* enabled (<code>cache.blocking=true</code>).
*/
public Object getFromCache(String key, int refreshPeriod) throws NeedsRefreshException {
return getCache().getFromCache(key, refreshPeriod);
}
Cache:
/**
* Retrieve an object from the cache specifying its key.
*
* @param key Key of the object in the cache.
* @param refreshPeriod How long before the object needs refresh. To
* allow the object to stay in the cache indefinitely, supply a value
* of {@link CacheEntry#INDEFINITE_EXPIRY}.
* @param cronExpiry A cron expression that specifies fixed date(s)
* and/or time(s) that this cache entry should
* expire on.
*
* @return The object from cache
*
* @throws NeedsRefreshException Thrown when the object either
* doesn't exist, or exists but is stale. When this exception occurs,
* the CacheEntry corresponding to the supplied key will be locked
* and other threads requesting this entry will potentially be blocked
* until the caller repopulates the cache. If the caller choses not
* to repopulate the cache, they <em>must</em> instead call
* {@link #cancelUpdate(String)}.
*/
public Object getFromCache(String key, int refreshPeriod, String cronExpiry) throws NeedsRefreshException {
CacheEntry cacheEntry = this.getCacheEntry(key, null, null); Object content = cacheEntry.getContent();
CacheMapAccessEventType accessEventType = CacheMapAccessEventType.HIT; boolean reload = false; // Check if this entry has expired or has not yet been added to the cache. If
// so, we need to decide whether to block, serve stale content or throw a
// NeedsRefreshException
if (this.isStale(cacheEntry, refreshPeriod, cronExpiry)) { //Get access to the EntryUpdateState instance and increment the usage count during the potential sleep
EntryUpdateState updateState = getUpdateState(key);
try {
synchronized (updateState) {
if (updateState.isAwaitingUpdate() || updateState.isCancelled()) {
// No one else is currently updating this entry - grab ownership
updateState.startUpdate(); if (cacheEntry.isNew()) {
accessEventType = CacheMapAccessEventType.MISS;
} else {
accessEventType = CacheMapAccessEventType.STALE_HIT;
}
} else if (updateState.isUpdating()) {
// Another thread is already updating the cache. We block if this
// is a new entry, or blocking mode is enabled. Either putInCache()
// or cancelUpdate() can cause this thread to resume.
if (cacheEntry.isNew() || blocking) {
do {
try {
updateState.wait();
} catch (InterruptedException e) {
}
} while (updateState.isUpdating()); if (updateState.isCancelled()) {
// The updating thread cancelled the update, let this one have a go.
// This increments the usage count for this EntryUpdateState instance
updateState.startUpdate(); if (cacheEntry.isNew()) {
accessEventType = CacheMapAccessEventType.MISS;
} else {
accessEventType = CacheMapAccessEventType.STALE_HIT;
}
} else if (updateState.isComplete()) {
reload = true;
} else {
log.error("Invalid update state for cache entry " + key);
}
}
} else {
reload = true;
}
}
} finally {
//Make sure we release the usage count for this EntryUpdateState since we don't use it anymore. If the current thread started the update, then the counter was
//increased by one in startUpdate()
releaseUpdateState(updateState, key);
}
} // If reload is true then another thread must have successfully rebuilt the cache entry
if (reload) {
cacheEntry = (CacheEntry) cacheMap.get(key); if (cacheEntry != null) {
content = cacheEntry.getContent();
} else {
log.error("Could not reload cache entry after waiting for it to be rebuilt");
}
} dispatchCacheMapAccessEvent(accessEventType, cacheEntry, null); // If we didn't end up getting a hit then we need to throw a NRE
if (accessEventType != CacheMapAccessEventType.HIT) {
throw new NeedsRefreshException(content);
} return content;
}
继续进入getCacheEntry方法:
/**
* Get an entry from this cache or create one if it doesn't exist.
*
* @param key The key of the cache entry
* @param policy Object that implements refresh policy logic
* @param origin The origin of request (optional)
* @return CacheEntry for the specified key.
*/
protected CacheEntry getCacheEntry(String key, EntryRefreshPolicy policy, String origin) {
CacheEntry cacheEntry = null; // Verify that the key is valid
if ((key == null) || (key.length() == 0)) {
throw new IllegalArgumentException("getCacheEntry called with an empty or null key");
} cacheEntry = (CacheEntry) cacheMap.get(key); // if the cache entry does not exist, create a new one
if (cacheEntry == null) {
if (log.isDebugEnabled()) {
log.debug("No cache entry exists for key='" + key + "', creating");
} cacheEntry = new CacheEntry(key, policy);
} return cacheEntry;
}
跟到这里,终于出现正主cacheMap:
/**
* The actual cache map. This is where the cached objects are held.
*/
private AbstractConcurrentReadCache cacheMap = null;
我们看下这个类AbstractConcurrentReadCache:
/**
* A version of Hashtable that supports mostly-concurrent reading, but exclusive writing.
* Because reads are not limited to periods
* without writes, a concurrent reader policy is weaker than a classic
* reader/writer policy, but is generally faster and allows more
* concurrency. This class is a good choice especially for tables that
* are mainly created by one thread during the start-up phase of a
* program, and from then on, are mainly read (with perhaps occasional
* additions or removals) in many threads. If you also need concurrency
* among writes, consider instead using ConcurrentHashMap.
* <p>
*
* Successful retrievals using get(key) and containsKey(key) usually
* run without locking. Unsuccessful ones (i.e., when the key is not
* present) do involve brief synchronization (locking). Also, the
* size and isEmpty methods are always synchronized.
*
* <p> Because retrieval operations can ordinarily overlap with
* writing operations (i.e., put, remove, and their derivatives),
* retrievals can only be guaranteed to return the results of the most
* recently <em>completed</em> operations holding upon their
* onset. Retrieval operations may or may not return results
* reflecting in-progress writing operations. However, the retrieval
* operations do always return consistent results -- either those
* holding before any single modification or after it, but never a
* nonsense result. For aggregate operations such as putAll and
* clear, concurrent reads may reflect insertion or removal of only
* some entries. In those rare contexts in which you use a hash table
* to synchronize operations across threads (for example, to prevent
* reads until after clears), you should either encase operations
* in synchronized blocks, or instead use java.util.Hashtable.
*
* <p>
*
* This class also supports optional guaranteed
* exclusive reads, simply by surrounding a call within a synchronized
* block, as in <br>
* <code>AbstractConcurrentReadCache t; ... Object v; <br>
* synchronized(t) { v = t.get(k); } </code> <br>
*
* But this is not usually necessary in practice. For
* example, it is generally inefficient to write:
*
* <pre>
* AbstractConcurrentReadCache t; ... // Inefficient version
* Object key; ...
* Object value; ...
* synchronized(t) {
* if (!t.containsKey(key))
* t.put(key, value);
* // other code if not previously present
* }
* else {
* // other code if it was previously present
* }
* }
*</pre>
* Instead, just take advantage of the fact that put returns
* null if the key was not previously present:
* <pre>
* AbstractConcurrentReadCache t; ... // Use this instead
* Object key; ...
* Object value; ...
* Object oldValue = t.put(key, value);
* if (oldValue == null) {
* // other code if not previously present
* }
* else {
* // other code if it was previously present
* }
*</pre>
* <p>
*
* Iterators and Enumerations (i.e., those returned by
* keySet().iterator(), entrySet().iterator(), values().iterator(),
* keys(), and elements()) return elements reflecting the state of the
* hash table at some point at or since the creation of the
* iterator/enumeration. They will return at most one instance of
* each element (via next()/nextElement()), but might or might not
* reflect puts and removes that have been processed since they were
* created. They do <em>not</em> throw ConcurrentModificationException.
* However, these iterators are designed to be used by only one
* thread at a time. Sharing an iterator across multiple threads may
* lead to unpredictable results if the table is being concurrently
* modified. Again, you can ensure interference-free iteration by
* enclosing the iteration in a synchronized block. <p>
*
* This class may be used as a direct replacement for any use of
* java.util.Hashtable that does not depend on readers being blocked
* during updates. Like Hashtable but unlike java.util.HashMap,
* this class does NOT allow <tt>null</tt> to be used as a key or
* value. This class is also typically faster than ConcurrentHashMap
* when there is usually only one thread updating the table, but
* possibly many retrieving values from it.
* <p>
*
* Implementation note: A slightly faster implementation of
* this class will be possible once planned Java Memory Model
* revisions are in place.
*
* <p>[<a href="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html"> Introduction to this package. </a>]
**/
public abstract class AbstractConcurrentReadCache extends AbstractMap implements Map, Cloneable, Serializable
类注释说明AbstractConcurrentReadCache是一个HashTable的版本,支持读多写少的应用场景,基本上缓存都是用户读多写少的场景。接下来我们再看下oscache怎么实例化的。还是从GeneralCacheAdministrator入手:
/**
* Create the cache administrator.
*/
public GeneralCacheAdministrator() {
this(null);
} /**
* Create the cache administrator with the specified properties
*/
public GeneralCacheAdministrator(Properties p) {
super(p);
log.info("Constructed GeneralCacheAdministrator()");
createCache();
}
它的构造函数调用了父类AbstractCacheAdministrator的构造函数:
/**
* Create the AbstractCacheAdministrator.
*
* @param p the configuration properties for this cache.
*/
protected AbstractCacheAdministrator(Properties p) {
loadProps(p);
initCacheParameters(); if (log.isDebugEnabled()) {
log.debug("Constructed AbstractCacheAdministrator()");
}
}
我们看它怎么加载配置文件的:
/**
* Load the properties file from the classpath.
*/
private void loadProps(Properties p) {
config = new Config(p);
}
进入Config类:
/**
* Create an OSCache configuration with the specified properties.
* Note that it is the responsibility of the caller to provide valid
* properties as no error checking is done to ensure that required
* keys are present. If you're unsure of what keys should be present,
* have a look at a sample oscache.properties file.
*
* @param p The properties to use for this configuration. If null,
* then the default properties are loaded from the <code>oscache.properties</code>
* file.
*/
public Config(Properties p) {
if (log.isDebugEnabled()) {
log.debug("OSCache: Config called");
} if (p == null) {
this.properties = loadProperties(PROPERTIES_FILENAME, "the default configuration");
} else {
this.properties = p;
}
}
千呼万唤始出来,默认配置文件oscache.properties:
/**
* Name of the properties file.
*/
private final static String PROPERTIES_FILENAME = "/oscache.properties";
oscache源码浅析的更多相关文章
- 【深入浅出jQuery】源码浅析--整体架构
最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...
- 【深入浅出jQuery】源码浅析2--奇技淫巧
最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...
- Struts2源码浅析-ConfigurationProvider
ConfigurationProvider接口 主要完成struts配置文件 加载 注册过程 ConfigurationProvider接口定义 public interface Configurat ...
- (转)【深入浅出jQuery】源码浅析2--奇技淫巧
[深入浅出jQuery]源码浅析2--奇技淫巧 http://www.cnblogs.com/coco1s/p/5303041.html
- HashSet其实就那么一回事儿之源码浅析
上篇文章<HashMap其实就那么一回事儿之源码浅析>介绍了hashMap, 本次将带大家看看HashSet, HashSet其实就是基于HashMap实现, 因此,熟悉了HashMap ...
- Android 手势识别类 ( 三 ) GestureDetector 源码浅析
前言:上 篇介绍了提供手势绘制的视图平台GestureOverlayView,但是在视图平台上绘制出的手势,是需要存储以及在必要的利用时加载取出手势.所 以,用户绘制出的一个完整的手势是需要一定的代码 ...
- Android开发之Theme、Style探索及源码浅析
1 背景 前段时间群里有伙伴问到了关于Android开发中Theme与Style的问题,当然,这类东西在网上随便一搜一大把模板,所以关于怎么用的问题我想这里也就不做太多的说明了,我们这里把重点放在理解 ...
- 【深入浅出jQuery】源码浅析2--使用技巧
最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...
- Android手势源码浅析-----手势绘制(GestureOverlayView)
Android手势源码浅析-----手势绘制(GestureOverlayView)
随机推荐
- 《大型网站系统与JAVA中间件实践》读书笔记-数据访问层
数据访问层 5.1.2数据库垂直/水平拆分的困难 随着网站业务的快速发展,数据量和访问量不断上升,数据库的压力越来越大. 更换更好的硬件(Scale Up)是一种解决方案,而且在我们能付得起硬件费用并 ...
- scala学习手记9 - =和==
= 赋值运算 scala的赋值运算和java的有着很大的不同.如a=b这样的赋值运算,在Java中返回值是a的值,在scala中返回的则是Unit(Unit是值类型,全局只存在唯一的值,即(),通常U ...
- 基础的JavaScript函数
基础的JavaScript函数 1.首字母大写 2.去除数组重复项 3.数组的排序 4.闭包 1. 把用户输入的不规范的英文名字,变为首字母大写,其他小写的规范名字(使用JavaScript的map函 ...
- mysql测试工具
Super Smack安装和使用 . sysbench
- C#—序列化(Serialize)和反序列化(NonSerialize)
(转自:http://www.cnblogs.com/Abel-Zhang/p/Serialize.html) 一.概述 序列化是把对象转变成流.相反的过程就是反序列化. 哪些场合用到这项技术呢? 1 ...
- UI-定时器与动画使用总结
#pragma mark - 定时器 ******************************************************************************* ...
- 【python】ConfigParser写入和读取配置文件
参照博客 http://www.cnblogs.com/TankXiao/p/3038350.html 配置文件格式: [section1] name = tank age = 28 [section ...
- Android OpenCSV
OpenCSV https://sourceforge.net/projects/opencsv/ 使用参考 http://stackoverflow.com/questions/16672074/i ...
- shfileoperation 删除文件 FileDelete(CString strName)
From:http://blog.csdn.net/lvwx369/article/details/41440883 注意:其中namePath 为全局变量 Cstring namePath; BOO ...
- 【转载】 用 Windows API “GetAdaptersInfo” 获取 MAC 时遇到的问题
From:http://blog.csdn.net/weiyumingwww/article/details/17554461 前段时间有个项目需要获取客户端的 MAC 地址,用作统计去重的参考数据. ...