`
zqhxuyuan
  • 浏览: 32288 次
  • 性别: Icon_minigender_1
  • 来自: 福建
社区版块
存档分类
最新评论

OSCache 应用与源码分析

阅读更多
缓存配置oscache.properties
cache.memory 是否使用内存缓存; true 或 false。默认为true; 如设置为false,那cache只能缓存到数据库或硬盘中。
cache.capacity 缓存的最大数量。默认是不限制,cache不会移走任何缓存内容。负数也被视不限制。
cache.algorithm 运算规则。为了使用规则,cache.capacity必须是指定的。有下面三种规则:
com.opensymphony.oscache.base.algorithm.LRUCache: last in first out(最后插入的最先调用)。
com.opensymphony.oscache.base.algorithm.FIFOCache: first int first out(最先插入的最先调用)。
com.opensymphony.oscache.base.algorithm.UnlimitedCache: cache中的内容将永远不会被丢弃。 默认选项
如果不指定cache.capacity,它将被设为默认选项即不限制。
cache.blocking 是否同步。true 或者 false。一般设为true,避免读取脏数据。
cache.unlimited.disk 指定硬盘缓存是否要作限制。默认false,disk cache capacity 和cache.capacity的值相同。
cache.persistence.class 指定类是被持久化缓存的类。class必须实现PersistenceListener接口。
作为硬盘持久,可以实现HashDiskPersistenceListener接口。
注意:HashDiskPersistenceListener 和 DiskPersistenceListener 需要设定硬盘路径:cache.path。
cache.path 硬盘缓存的路径。目录如果不存在将被建立。注意oscache应该要有权限写文件系统。
cache.persistence.overflow.only 是否只有在内存不足的情况下才使用硬盘缓存。默认值false。
如果内存cache被允许的话(cache.memore=true),推荐设置为true。
cache.event.listeners 用逗号隔开的class名列表,默认是不配置任何class的。
每个class必须实现以下接口中的一个或者几个CacheEntryEventListener:接收cache add/update/flush/remove事件
CacheMapAccessEventListener :接收cache访问事件。这个可以让你跟踪cache怎么工作。
com.opensymphony.oscache.plugins.clustersupport.BroadcastingCacheEventListener : 分布式的监听器。可以广播到局域网内的其他cache实例。
com.opensymphony.oscache.extra.CacheEntryEventListenerImpl :一个简单的监听器。在cache的生命周期中记录所有entry的事件。
com.opensymphony.oscache.extra.CacheMapAccessEventListenerImpl : 记录count of cache map events(cache hits,misses and state hits)
cache.key 在application 和 session的作用域时用于标识cache 对象的。默认值:"__oscache_cache"
如果代码中需要用到默认值时可以通使用com.opensymphony.oscache.base.Const.DEFAULT_CACHE_KEY 来取得
cache.use.host.domain.in.key 当配置多个服务器时,想通过服备器名称自动生成cache key时,可将此属性设为true. 默认值为false
核心类和概念
角色 作用
cache factory
缓存管理
AbstractCacheAdministrator
GeneralCacheAdministrator
ServletCacheAdministrator
生产Cache,同时管理用户配置的config,配置监听器列表和Cache的绑定。
负责获得 Cache Proxy ,兼有一些对 Cache Proxy 的管理功能。
子类ServletCacheAdministrator关联了一个ServletContext,以实现在Web容器中的存储和传值
cache proxy
缓存接口
Cache
ServletCache
OSCache缓存管理的核心,也是cache map的存放场所。
Cache Map 的代理,它主要负责从 Cache Map 中取得 存储指定的缓存对象,
如果缓存对象过期,那么就将缓存刷新,并向指定的监听者发送存 取事件。
子类ServletCache引入了一个scope的概念,用以管理不同的可见性缓存,存在application session级别。
cache map
缓存
AbstractConcurrentReadCache
FIFOCache LRUCacheUnlimitedCache
缓存存储map。存储了所有的缓存实体,是一个 OSCache 专有的 Map 实现,
根据指定的算法清除缓存,以及将缓存持久化到磁盘中。
cache entry
缓存条目
CacheEntry
map中存储的每一项。其内部包含了缓存条目的创建、修改时间,存储的key、value,group等属性
缓存对象的包装实体,它包装了缓存对象和刷新策略。
cache map 对 cache entry的管理
EntryUpdateState是cache entry当前所处状态的表示,OSCache尽量避免了使用synchronize,引入了许多状态参量。
 

 

 
 
典型应用场景
典型的“缓存对象”场景是:
应用调用Cache Factory获得Cache Proxy,然后应用将要缓存的对象以及刷新策略通过Cache Proxy存储到Cache Map中,并通知各个Listener。
 
典型的“取得缓存对象”的场景是:
应用调用 Cache Factory 获得 Cache Proxy ,然后给 Cache Proxy 的相应方法传入要获得的缓存对象的 key ,
Cache Proxy 会根据指定的刷新策略判断缓存是否过期,如果缓存没有过期,则返回缓存对象,如果缓存过期,则刷新缓存,
并向应用层抛出需要刷新的异常NRE,应用如果收到此异常,将重新计算内容并将内容缓存。
 
在缓存条目过期或者不存在的时候都会抛出NeedsRefreshException。
当这个异常出现,相应的cache map的key会被锁住,并且要访问它的所有其他线程都被block住了,
所以,这个时候一定要调用putInCache或者cancelUpdate,千万不能遗漏,否则就是一个死锁。
 
缓存刷新处理
在catch(NeedRefreshException)中进行 putInCache,又在putInCache的catch(Exception)中做cancelUpdate,因为我们不希望看到一个线程在 getFromCache的位置一直阻塞下去。
GeneralCacheAdministrator admin = new GeneralCacheAdministrator();
admin.putInCache("myKey", "HelloWorld"); ①
 
String myKey = "myKey";
String myValue;
int myRefreshPeriod = 1000;
try {
    // Get from the cache
    myValue = (String) admin.getFromCache(myKey, myRefreshPeriod); ②
} catch (NeedsRefreshException nre) {
    try {
        // Get the value (probably from the database)
        myValue = "This is the content retrieved." ;
        // Store in the cache
        admin.putInCache(myKey, myValue); ③
    } catch (Exception ex) {
        // We have the current content if we want fail-over.
        myValue = (String) nre.getCacheContent();
        // It is essential that cancelUpdate is called if the
        // cached content is not rebuilt
        admin.cancelUpdate(myKey);
    }
}
从对象缓存map中取出myKey对应的对象,两种情况可能发生: 
1 如果对象存在(先前putInCache)且没有过期,getFromCache正常返回。
2 如果对应的对象不存在或者过期,需要刷新缓存:      
   1) 请求的线程第一个探测到对象过期,抛给client一个NRE, 
         提示client需要对数据进行刷新putInCache。    
   2) 如果请求的线程并非第一个探测到对象不存在
          A.之前探测到的线程没有进行刷新处理,直接阻塞。
          B.之前探测到的线程进行了刷新处理,抛出NRE。
   3) 如果请求的线程并非第一个探测到对象过期
          A.之前探测到的线程没有进行刷新处理,直接阻塞。
          B.之前探测到的线程进行了刷新处理:
               I.如果blocking=false 抛出NRE,采取补救措施取可能过期内容 
              II.如果blocking=true,那么该线程会在此阻塞,
                  直到putInCache/cancelUpdate在另一个线程中被调用。
 
源码分析
Cache管理类
// 私有化Cache实例,不直接操作Cache实例,而是通过管理类对缓存管理:put,get,flush。当然也可以通过getCache获取Cache实例对Cache操作
private Cache applicationCache = null
 
public GeneralCacheAdministrator(Properties p) {
    super (p); //调用父类AbstractCacheAdministrator的构造函数
    createCache();
}
 
protected AbstractCacheAdministrator(Properties p) {
    loadProps(p); //加载配置文件,封装到对象 Config里(通过 Config获取配置信息)
    initCacheParameters(); //初始化参数,将 Config获取到的配置信息赋值给几个重要的属性,目的是在类或子类中可以直接使用已经赋值好的变量
}
 
private void createCache () {
    applicationCache = new Cache(isMemoryCaching(), isUnlimitedDiskCache(), isOverflowPersistence(), isBlocking(), algorithmClass , cacheCapacity );    
    //创建完Cache实例后,将配置文件配置的监听器应用到Cache上
    configureStandardListeners( applicationCache);
}
 
getFromCache 
读取缓存数据,指的是从CacheMap[缓存]中获取对应的CacheEntry[缓存条目],再获取CacheEntry的内容[缓存对象]
 
updateStates:map对象。它提供了cache中的所有被同步访问的cacheEntry对象的状态集合,用来协调并发访问(修改/读取)同一个缓存条目。
key就是cache中该cacheEntry对象对应的key。value是updatestate 对象。
 

OSCache采用引用计数状态量机制,解决了多线程并发访问缓存的问题,同时,没有任何语句锁住整个cache map,在高并发的情况下不会有太大的性能损失。

当一个过期的缓存条目被请求,或者观察到缓存未命中,cache会检查这个map
当一条缓存条目正在被另一个线程更新,那么有两种策略,根据配置项cache.blocking的配置,
要么等待更新完成(阻塞策略),要么返回已经过时的缓存内容(非阻塞策略)。

为了避免数据争用(并发读取),map里面的值在某线程操作的过程中不能消失,
因此updateStates实际的作用是显式引用计数(每一个updateState里面都有一个计数器),
在所有线程都对指定的key完成存取和更新以后,map的这条entry才能被清除掉。

updateStates存储的是所有 处于更新状态的(UPDATE_IN_PROGRESS) 缓存条目,   
也就是说如果某一个缓存条目没有被更新(NOT_YET_UPDATING),则不会存放在这个map里。      
或者当前缓存条目更新完毕(UPDATE_COMPLETE),则被从这个map里踢除掉,因为不再处于正在更新的状态!
 
根据key读取CacheMap中的缓存对象
[1] 缓存中不存在key对应的缓存条目,都没有怎么获取?
[2] key对应的updateState已经存在,说明获取该key对象的线程又多了一个
[3] updateState为等待更新或被取消更新,设置状态为正在更新,并将线程引用计数+1,抛出NRE异常,提示客户端刷新缓存
[4] 缓存过期,如果缓存内容为空,访问类型为未命中,不为空,访问类型为命中过期数据,都会抛出NRE异常
[5] 缓存数据存在,如果没有过期,直接返回。如果过期,当其他线程正在更新,如果配置blocking=true则等待更新完成并获取最新数据,否则直接返回过期数据。
[6] 其他线程正在更新缓存,但是又取消了更新,当前线程设置状态为正在更新,并将线程引用计数+1,抛出NRE异常,提示客户端刷新缓存
[7] 同步块结束后,将[2]读取缓存的线程计数-1,如果没有线程引用updateState,则释放updateState对象
[8] 客户端捕获到NRE异常,刷新缓存,可以调用putInCache()或cancleUpdate(),将引用计数-1,释放updateState对象
 
读取流程
判断缓存是否过期,如果过期,那么会从更新状态缓存中查找更新状态,如果没查到那么创建一个,如果已经存在了,将引用计数+1。 ->getUpdateState(key)
如果缓存还没有进行更新,那么将更新状态设置为UPDATE_IN_PROGRESS,并且再次将引用计数+1,此时: 
(1) 在设定完更新状态后,读取缓存的线程会将引用计数-1,如果没有线程此时再引用了那么在更新状态缓存中移除此项。 -> finally里
(2) 如果其他线程在更新缓存的时候会将引用计数-1,如果没有线程此时再引用了那么在更新状态缓存中移除此项。 
这样,通过上边的两个过程,就将引用计数变为0了。
 
public Object getFromCache(String key, int refreshPeriod, String cronExpiry) throws NeedsRefreshException {
     // 先尝试在缓存CacheMap中查找。和putInCache一样,如果存在则返回缓存条目,
    // 不存在则新建一个缓存条目并返回.因为缓存CacheMap中不存在这个缓存条目,缓存对象也为空[1]
    CacheEntry cacheEntry = this.getCacheEntry(key, null, null);
 
    Object content = cacheEntry.getContent(); // 缓存内容,实际缓存的对象[5]
    CacheMapAccessEventType accessEventType = CacheMapAccessEventType. HIT; // 缓存访问事件类型,默认是"缓存命中"
 
    boolean reload = false; //是否重新加载,如果另外的线程正在更新,等待更新完毕后,reload=true,重加获取缓存里的对象
 
    // Check if this entry has expired or has not yet been added to the cache.If so, 缓存条目过期/还没加入到缓存中[1]
    // we need to decide whether to block, serve stale content or throw a NeedsRefreshException 阻塞,获取过期数据,抛出NRE
    if ( this.isStale(cacheEntry, refreshPeriod, cronExpiry)) {
        // Get access to the EntryUpdateState instance and increment the usage count during the potential sleep
        // 检测updateStates中是否存在该key,如果没有,则为该key新建一个EntryUpdateState对象并将其put到updateStates中;
        // 如果存在该key,则需要为该对象的同时被访问的线程计数+1,表示读取该key对象的线程又多了一个[2:+1]
        EntryUpdateState updateState = getUpdateState(key);
        try {
            // 住updateState,即对updateState进行同步
            synchronized (updateState) { 
                // A.若状态为"等待更新",则该对象尚未被其他线程访问
                // A.若状态为"被取消",则表明已有其他的线程对该CacheEntry对象进行访问,但当前非"正在被更新"的状态
                if (updateState.isAwaitingUpdate() || updateState.isCancelled()) {
                    // No one else is currently updating this entry - grab ownership
                    // 将更新状态设置为正在更新UPDATE_IN_PROGRESS,再次将线程引用计数+1[3:+1] 
                    // 只是更新状态并没有真正更新,再说了这里只是读取操作所以肯定不会更新 
                    // 先了解下此时状态:等待更新,实际是等待被更新(被动的,读取操作显然不会主动更新,而是客户端主动更新)
                    // 这里引用计数+1,只是提示客户端更新缓存条目,当客户端更新完缓存条目,会将引用计数-1,这样就完成了一次存取操作
                    updateState.startUpdate(); 
                    
                    // 新建缓存条目,还没放入缓存中[1],缓存的对象内容为空,因此将此次访问记录为未命中(缓存中都没有数据,显然没有命中)
                    if (cacheEntry.isNew()) {
                        accessEventType = CacheMapAccessEventType. MISS;
                    } 
                    // 若对象内容不为空,则表明该次访问的数据已经过期,记录为命中过期数据(缓存中有数据但是过期了)
                    // 进入同步块时判断缓存条目是否过期当数据有效时,执行到这里,既然不是新建的缓存条目,就一定是过期的数据
                    else {
                        accessEventType = CacheMapAccessEventType. STALE_HIT;
                    }
                    // 这两种类型,在后面的处理中[4],都会抛出NRE异常,提示客户端刷新缓存。未命中和命中过期数据都不是我们想要的。
                }
                // B.若状态为"正在更新",表明有其他线程正在对该对象进行更新
                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.
                     // 在配置为阻塞策略的情况下,或这是一个新建的缓存条目[1],都会阻塞,等待更新完成后notify
 
                    // 1:其他线程正在对该对象进行更新,是否需要等待其他线程更新完毕读取最新数据,或者直接返回过期数据[5]
                    // 2:其他线程取消对该对象更新,此时需要抛出NRE异常;
                    // 3:其他线程对该对象的更新完毕,此时可以返回最新数据。
                    if (cacheEntry.isNew() || blocking) {
                        do {
                            try {
                                // 如果对象调用了wait方法就会使持有该对象的线程把该对象的控制权交出去(交给正在更新该缓存条目的线程),然后处于等待状态
                                // 当更新缓存条目的线程更新完毕,即在putInCache()的completeUpdate(key)中调用updateState.notifyAll()
                                // notify方法就会通知某个正在等待这个对象的控制权的线程可以继续运行,
                                // 即当前线程继续判断updateState的状态,两种状态:更新被取消或更新完成
                                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 引用计数+1[6:+1]
                            updateState.startUpdate();
                           
                            if (cacheEntry.isNew()) {
                                accessEventType = CacheMapAccessEventType. MISS;
                            } else {
                                accessEventType = CacheMapAccessEventType.STALE_HIT ;
                            }
                            // 抛出NRE异常[4]
                        } 
                        // 如果没取消更新的话,那么这时updateState的状态应该是complete,即其他线程更新完毕,此时可以返回最新数据
                        // 其他线程在更新完毕时,会将同步访问计数-1,表明一个线程对该对象的访问结束。
                        else if (updateState.isComplete()) {
                            reload = true;
                        } else {
                            log.error("Invalid update state for cache entry " + key);
                        }
                    }
                    // 如果缓存中有数据(此时是过期的了),且blocking=false,下面的逻辑除了finally外都不执行,返回过期数据content[5]
                } 
                // C.若状态为"完成更新",则表明有其他线程调用了completeUpdate(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()
            // 在设定完更新状态后,读取缓存的线程会将引用计数-1[7:-1]对应[2:+1]
            // 如果没有线程此时再引用了,updateStates就会移除此项updateState,释放updateState对象
            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 如果没有命中数据(未命中或命中过期),抛给客户端需要刷新数据的异常
    // 上面updateState.startUpdate()提示客户端刷新缓存,都会执行到这里[4]
    if (accessEventType != CacheMapAccessEventType. HIT) { 
        throw new NeedsRefreshException(content);
    }
    // 当抛出异常时,没有将同步访问计数减一,因此在设置状态为“正在更新”时增加的计数不会被减去,该 updateState对象将被保留在 updateStates
    // 在捕获NRE异常的地方一定要调用completeUpdate或cancleUpdate方法,将计数-1[8:-1]对应[3:+1]或[6:+1],这样就会释放 updateState对象
 
    return content;
}
 
putInCache写入缓存
public void putInCache(String key, Object content, String[] groups, EntryRefreshPolicy policy, String origin) {
    // 首先查找这个key对应的缓存条目在缓存中是否已经存在,没有就新建CacheEntry,注意此时缓存对象的内容还没有设置
    CacheEntry cacheEntry = this.getCacheEntry(key, policy, origin);
   
    // 判断是否是新创建的缓存
    boolean isNewEntry = cacheEntry.isNew();
    // [CACHE-118] If we have an existing entry, create a new CacheEntry so we can still access the old one later
    // 如果不是新的缓存也会新建一个CacheEntry,因为老的缓存值在后边也能访问到
    if (!isNewEntry) {
        cacheEntry = new CacheEntry(key, policy);
    }
 
    cacheEntry.setContent(content);
    cacheEntry.setGroups(groups);
    cacheMap.put(key, cacheEntry); // 放入缓存:将content放入CacheEntry,将CacheEntry放入CacheMap
 
    // Signal to any threads waiting on this update that it's now ready for them in the cache!
    // 通知其他在等待值的线程结束等待,现在该key对应的缓存条目已经准备好了,其他线程可以获取最新的缓存内容了
    completeUpdate(key);
 
    // 针对缓存事件的listener发送事件
    if ( listenerList.getListenerCount() > 0) {
        CacheEntryEvent event = new CacheEntryEvent(this, cacheEntry, origin);
 
        if (isNewEntry) {
            dispatchCacheEntryEvent(CacheEntryEventType. ENTRY_ADDED, event); // 新添加缓存
        } else {
            dispatchCacheEntryEvent(CacheEntryEventType. ENTRY_UPDATED, event); // 更新缓存
        }
    }
}
 
 

Web应用集成
缓存对象:直接调用API的接口即可
缓存部分页面
复制oscache.tld到WEB-INF下,在web.xml添加
<taglib>
    <taglib-uri>oscache</taglib-uri>
    <taglib-location>/WEB-INF/oscache.tld</taglib-location>
 </taglib>
在jsp页面使用标签<%@ taglib uri="oscache" prefix="cache"%>
使用Oscache的标签<oscache></oscache>来进行页面的局部缓存

缓存整个页面:用CashFilter实现页面级缓存,可缓存单个文件、缓存URL pattern和自己设定缓存属性的缓存。
主要用于对web应用中的某些动态页面进行缓存,尤其是那些需要生成PDF格式文件/报表、图片文件等的页面,
不仅减少了数据库的交互、减少数据库服务器的压力,而且对于减少web服务器的性能消耗有很显著的效果。

在web.xml中进行配置来决定缓存哪一个或者一组页面,而且还可以设置缓存的相关属性。
[注] 只有客户访问时返回http头信息中代码为200(也就是访问已经成功)的页面信息才能够被缓存。
<filter>
    <filter-name>CacheFilter</filter-name>
    <filter-class>com.opensymphony.oscache.web.filter.CacheFilter</filter-class>
    <init-param>
        <param-name>time</param-name>
        <param-value>600</param-value>
    </init-param>
    <init-param>
        <param-name>scope</param-name>
        <param-value>session</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>CacheFilter</filter-name>
    <url-pattern>*.jsp</url-pattern> <!-- 对所有jsp页面内容进行缓存-->
</filter-mapping>
 
 
cache标签(缓存部分页面)
第一次请求到达时,标签中的内容被处理并且缓存起来,当下一个请求到达时,缓存系统会检查这部分内容的缓存是否已经失效,
如果符合下面四项中的任何一项,被缓存的内容视为已经失效,这时被缓存的内容将被重新处理并且返回处理过后的信息,
如果被缓存的内容没有失效,那么返回给用户的将是缓存中的信息。主要是以下几项:
(1)缓存时间超过了cache标签设置的time或者duration属性规定的超时时间 
(2)cron属性规定的时间比缓存信息的开始时间更晚 
(3)标签中缓存的内容在缓存后又被重新刷新过 
(4)其他缓存超期设定 
key
标识缓存内容的关键词。在指定的作用范围内必须是唯一的。默认的key是被访问页面的URI和后面的请求字符串。
scope 缓存发生作用的范围,application(默认)或者session
time
缓存内容的时间段,单位是秒,默认是3600秒,如果设定一个负值,那么这部分被缓存的内容将永远不过期。
duration
指定缓存内容失效的时间,是相对time的另一个选择,可以使用简单日期格式或者符合USO-8601的日期格式。如:duration='PT5M' duration='5s'
cron 指定缓存内容失效表达式
refresh
false/true。如果refresh属性设置为true,不管其他的属性是否符合条件,这部分被缓存的内容都将被更新,这给编程者一种选择,决定什么时候必须刷新。
mode
如果不希望被缓存的内容增加到给用户的响应中,可以设置mode属性为"silent"
此时被缓存的部分不在页面上显示,而其它任意的mode属性值都会将缓存的部分显示到页面上。
groups
指定当前cache标签所属的组,可使用“,”分割组名。这样就可以对缓存项进行分组了。
如果缓存项依赖于应用的其它部分或其它数据,分组就有了用武之地——当这种依赖改变时(刷新相关的组),这个组的所有缓存项都将过期。
language
使用ISO-639定义的语言码来发布不同的缓存内容(under an otherwise identical key)。
refreshpolicyclass
指定自定义的刷新策略类的全限定类名。这个类继承自com.opensymphony.oscache.web.WebEntryRefreshPolicy
refreshpolicyparam
指定任意需要传给refreshpolicyclass的参数。如果没有指定refreshpolicyclass,则这个值不起作用。
  • 大小: 38.6 KB
  • 大小: 8.7 KB
0
3
分享到:
评论

相关推荐

    应用OSCache提升J2EE系统运行性能

    在源码层面,OSCache的实现基于软引用和弱引用的概念,使得缓存能够在内存不足时自动释放,防止内存泄露。同时,它支持多级缓存、缓存同步、过期策略等功能,可以根据业务需求进行定制。开发者可以通过配置文件或者...

    oscache 集群和数据同步

    四、源码分析 osCache的源码提供了深入理解其内部工作原理的机会。通过阅读源码,我们可以了解到如何配置和自定义缓存策略,以及集群中的数据同步是如何实现的。例如,可以查看`CacheManager`类如何初始化和管理...

    Oscache攻略

    本文将深入探讨Oscache的核心功能、使用方法以及源码分析,帮助读者全面了解这一工具的精髓。 首先,我们要理解什么是缓存。缓存是一种存储技术,用于临时存储常用数据,以减少对主存储器或网络资源的访问,从而...

    OSCache 缓存对象的总结

    博客中可能还会深入到OSCache的源码解析,分析其内部实现机制,包括数据结构的选择、锁的使用以及如何实现高效的缓存替换算法等。通过源码阅读,开发者可以更好地理解OSCache的工作原理,并根据自身需求进行定制化...

    OSCACHE配置URL实现页面缓存的Fliter(修改配置无需重启)

    总结,OSCache配置URL实现页面缓存的Filter是一种有效的性能提升手段,通过动态配置和源码分析,我们可以深入了解其工作原理并优化Web应用的性能。同时,配合适当的工具,管理和监控缓存变得更为便捷。

    OSCache简介

    开发者可以通过分析这些文件,更深入地理解OSCache 的工作原理和实际应用。 总结来说,OSCache 是一个强大的Java缓存工具,它提供了丰富的功能和高度的灵活性,能够帮助开发者优化应用性能,减少数据库访问压力,...

    Oscache简单实例

    **源码分析** 对于开发者来说,了解Oscache的源码可以帮助深入理解其工作原理。源码中包含了`CacheManager`、`Cache`、`CacheEntry`等核心类,它们负责缓存的创建、管理和存储。通过对这些类的分析,可以学习到如何...

    jar 源码(spring+ibatis+..)

    标题中的"jar 源码(spring+ibatis+..)"指的是这个压缩包包含了与Java开发相关的开源框架Spring、MyBatis以及...通过阅读和分析这些源码,不仅可以提高编程技能,还能培养解决问题的能力,对个人的职业发展大有裨益。

    《hibernate逍遥游记》附属光盘之源码

    通过分析《Hibernate逍遥游记》的源码,读者不仅可以深入理解Hibernate的工作原理,还能掌握实际项目中如何高效地应用Hibernate进行数据访问层的设计和开发。这些源码实例为学习者提供了一个实战演练的平台,有助于...

    带二级缓存和业务封装的SSH项目源码

    SSH(Struts2 + Spring + Hibernate)是一个经典的Java Web开发框架,它整合了三大主流的开源框架,...通过分析和实践这个项目,开发者可以提升自己的技能,了解如何在实际项目中应用这些技术来构建高效、稳定的系统。

    hibernate3源码

    通过阅读和分析这个Hibernate3的源代码,开发者可以深入了解其实现机制,比如查询优化、缓存策略、事务管理等,这对于提升Java应用的性能和开发效率具有很大的价值。对于想要深入学习Hibernate或ORM技术的Java开发者...

    Hibernate教程25_Hibernate缓存

    **描述分析:** 描述中的链接指向了一个CSDN博客文章,虽然具体内容没有给出,但可以推测它提供了与Hibernate缓存相关的源码示例。这意味着我们可能要探讨的是如何在实际项目中应用Hibernate的缓存技术,并通过代码...

Global site tag (gtag.js) - Google Analytics