`
zddava
  • 浏览: 243613 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

oscache源代码阅读(二) -- 从缓存中读取pojo

阅读更多
现在开始要介绍的从缓存中读取数据的过程,还是在GeneralCacheAdministrator#getFromCache(),这里有3个同名方法,还是找一个参数最多的:

	/**
	 * 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}
	 * @param cronExpression
	 *            A cron expression that the age of the cache entry will be
	 *            compared to. If the entry is older than the most recent match
	 *            for the cron expression, the entry will be considered stale.
	 * @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, String cronExpression)
			throws NeedsRefreshException {
		return getCache().getFromCache(key, refreshPeriod, cronExpression);
	}


还是来到了Cache的#getFromCache():

	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;

		// 判断缓存是否过期
		if (this.isStale(cacheEntry, refreshPeriod, cronExpiry)) {
			// 得到缓存更新状态
			EntryUpdateState updateState = getUpdateState(key);
			try {
				synchronized (updateState) {
					if (updateState.isAwaitingUpdate() || updateState.isCancelled()) {
						// 如果是等待更新或者取消更新,那么将更新状态设置成UPDATE_IN_PROGRESS
						updateState.startUpdate();

						if (cacheEntry.isNew()) {// 如果缓存中没有,那么设成"没命中"
							accessEventType = CacheMapAccessEventType.MISS;
						} else {// 设成"命中过期数据",这个后边要抛异常的
							accessEventType = CacheMapAccessEventType.STALE_HIT;
						}
					} else if (updateState.isUpdating()) {
						// 如果其他线程正在更新缓存中,那么wait,等到更新完成后notify
						if (cacheEntry.isNew() || blocking) {
							do {
								try {
									updateState.wait();
								} catch (InterruptedException e) {
								}
							} while (updateState.isUpdating());

							if (updateState.isCancelled()) {
								// 如果更新的线程取消了更新,那么缓存仍然是过期的,执行和第一个分支相同的操作
								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 {
				// 线程引用数减少1
				releaseUpdateState(updateState, key);
			}
		}

		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 (accessEventType != CacheMapAccessEventType.HIT) {
			throw new NeedsRefreshException(content);
		}

		return content;
	}


整个流程还是很好理解的,这里看一些细节,首先是#isStale()方法:

	protected boolean isStale(CacheEntry cacheEntry, int refreshPeriod, String cronExpiry) {
		// 判断是否需要刷新
		boolean result = cacheEntry.needsRefresh(refreshPeriod) || isFlushed(cacheEntry);

		if ((!result) && (cronExpiry != null) && (cronExpiry.length() > 0)) {
			try {
				// 利用计划任务的方式处理过期
				FastCronParser parser = new FastCronParser(cronExpiry);
				result = result || parser.hasMoreRecentMatch(cacheEntry.getLastUpdate());
			} catch (ParseException e) {
				log.warn(e);
			}
		}

		return result;
	}


其次是获取更新状态的方法:

	protected EntryUpdateState getUpdateState(String key) {
		EntryUpdateState updateState;

		synchronized (updateStates) {
			// Try to find the matching state object in the updating entry map.
			updateState = (EntryUpdateState) updateStates.get(key);

			if (updateState == null) {
				// It's not there so add it.
				updateState = new EntryUpdateState();
				updateStates.put(key, updateState);
			} else {
				// Otherwise indicate that we start using it to prevent its
				// removal until all threads are done with it.
				updateState.incrementUsageCounter();
			}
		}

		return updateState;
	}


另外就是EntryUpdateState的startUpdate()方法,它会把过期缓存的状态设为UPDATE_IN_PROGRESS,这样更新缓存的线程就会执行更新操作了。

	public int startUpdate() {
		if ((state != NOT_YET_UPDATING) && (state != UPDATE_CANCELLED)) {
			throw new IllegalStateException("Cannot begin cache update - current state (" + state
					+ ") is not NOT_YET_UPDATING or UPDATE_CANCELLED");
		}

		state = UPDATE_IN_PROGRESS;
		return incrementUsageCounter();
	}


整个流程还是很好理解的,首先判断缓存是否过期,如果过期,那么会从更新状态缓存中查找更新状态,如果没查到那么创建一个,如果已经存在了,将引用计数加1。
如果缓存还没有进行更新,那么将更新状态设置为UPDATE_IN_PROGRESS,并且再次将引用计数加1,此时:
(1) 在设定完更新状态后,读取缓存的线程会将引用计数减1,如果没有线程此时再引用了那么在更新状态缓存中移除此项。
(2) 如果其他线程在更新缓存的时候会将引用计数减1,如果没有线程此时再引用了那么在更新状态缓存中移除此项。
这样,通过上边的两个过程,就将引用计数变为0了。

想法其实是非常好的,不过我在实际的测试中发现有时候其实并不是完全按照这个流程走下去的(当然很有可能是当初设计就是如此,允许发生报出这种异常):

线程1调用getFromCache()并发现缓存过期,更新状态为UPDATE_IN_PROGRESS后还没有调用#releaseUpdateState(),线程2执行#putInCache(),线程3执行#putInCache()。线程2更新完状态为UPDATE_COMPLETE后将引用计数减1,但是此时由于线程1还有一个引用计数,所以线程3更新状态时会在#completeUpdate()中抛出异常,因为此时的状态是UPDATE_COMPLETE。
1
1
分享到:
评论

相关推荐

    JavaEE源代码 oscache-2.1

    JavaEE源代码 oscache-2.1JavaEE源代码 oscache-2.1JavaEE源代码 oscache-2.1JavaEE源代码 oscache-2.1JavaEE源代码 oscache-2.1JavaEE源代码 oscache-2.1JavaEE源代码 oscache-2.1JavaEE源代码 oscache-2.1JavaEE源...

    oscache缓存技术应用

    - 将`oscache.properties`和`oscache.tld`放入`WEB-INF/classes`(或自动编译到此目录的源代码目录)。 - 配置`oscache.properties`以满足项目需求。 2. **使用方法** - **缓存对象**:通过调用API接口直接缓存...

    oscache-java缓存框架

    osCache是Java开发中常用的缓存框架之一,它主要用于提高应用程序的性能和效率,通过将数据存储在内存中,减少对数据库的访问。osCache不仅可以用于Web应用,也可以用于任何Java应用程序,支持集群环境,提供了丰富...

    oscache-2.1.jar

    oscache-2.1.jar oscache-2.1.jar

    oscache缓存技术

    压缩包中的代码示例可能包括了如何创建osCache实例、配置缓存、添加和检索缓存对象的步骤。通过学习这些示例,开发者可以更好地理解osCache的工作原理,并将其应用到实际项目中。 总结来说,osCache是一个强大的...

    oscache-2.4.1-full

    OSCache基于内存的缓存系统能够将经常访问的数据存储在内存中,避免了每次请求时都需要从数据库中获取数据的开销。通过缓存热点数据,它可以显著提升Web应用的响应速度,降低延迟,提高用户体验。 2. **JSP定制...

    oscache-JSP缓存

    在这个例子中,如果`myCachedData`不在缓存中,会调用`getExpensiveData()`方法获取数据并存入缓存,下次请求时直接从缓存读取,避免了重复计算。 **总结** osCache作为一款强大的缓存工具,为Java Web开发提供了...

    OsCache缓存框架使用示例

    OsCache是Java应用程序中常用的缓存框架,它能够有效地提高应用程序的性能,通过将经常访问的数据存储在内存中,减少对数据库或其他数据源的访问,从而降低系统负载。本示例将通过一个天气预报Web服务的场景,详细...

    oscache缓存配置

    在本例中,我们已经有了oscache-2.4.1.jar,这是一个包含osCache核心库的文件。在Java项目中,通常将其添加到项目的类路径(classpath)中,以便程序能够找到并使用osCache的相关类。 接着,我们需要配置osCache的...

    ajax4jsf-1.1.0.jar,oscache-2.3.2.jar

    这两个jar文件,"ajax4jsf-1.1.0.jar" 和 "oscache-2.3.2.jar",在开发过程中扮演着至关重要的角色。 Ajax4JSF,全称为Asynchronous JavaScript and XML for JavaServer Faces,是一个用于扩展JSF功能的开源库。它...

    oscache-2.4.1-full.rar

    (4) 支持集群:集群缓存数据能被单个的进行参数配置,不需要修改代码。 (5) 缓存过期:你可以有最大限度的控制缓存对象的过期,包括可插入式的刷新策略(如果默认性能不能满足需要时)。 3、OSCache的安装与配置...

    基于OSCache的页面缓存(收藏)

    页面缓存是指将频繁访问的网页内容存储在内存中,当用户请求这些页面时,不再需要重新生成或者从数据库查询数据,而是直接从缓存中读取,从而提高了响应速度。OSCache提供了一套完整的解决方案,包括缓存的创建、...

    一个OSCache缓存技术的关键zip包

    - 引入依赖:将oscache-2.0.2-22Jan04.jar添加到项目的类路径中。 - 配置:根据应用需求编辑oscache.properties,设置缓存策略、事件监听器等。 - 初始化:在应用程序启动时初始化OSCache,例如使用`...

    oscache的使用实例和详解

    - **移除数据**: 使用`remove(key)`方法从缓存中删除指定键的数据。 - **清空缓存**: 可以通过`clear()`方法清空整个缓存,或者使用`evictRegion(regionName)`方法清空特定区域的缓存。 ### 4. oscache的高级特性 ...

    oscache-2.1.1-full.zip_full_oscache_oscache 2_oscache2

    2. **src**: 源代码目录,提供了osCache的全部源代码,便于开发者深入研究和定制。 3. **www.pudn.com.txt**: 可能是下载来源的注释或说明文件。 4. **lib**: 库文件夹,包含osCache运行所依赖的JAR文件和其他第三...

    oscache-2.2jar包

    1. **oscache-2.2-rc.jar**:这是osCache的核心库文件,包含了所有用于缓存管理的类和方法。"rc"通常表示Release Candidate,意味着这是一个接近最终版本的测试版本,可能已经过广泛的测试,但还有可能存在的小问题...

    Hibernate OSCache缓存

    总之,Hibernate 结合 OSCache 实现的二级缓存能够显著提升数据读取效率,降低数据库压力,但需要注意合理配置和使用,以确保最佳性能和数据一致性。在实际项目中,应根据具体业务场景选择合适的缓存策略,并持续...

    hibernate+oscache实现二级缓存实例

    首先,我们需要在Hibernate的配置文件(如`hibernate.cfg.xml`)中启用二级缓存并指定OSCache为缓存提供者。添加如下配置: ```xml &lt;property name="hibernate.cache.use_second_level_cache"&gt;true ...

Global site tag (gtag.js) - Google Analytics