cache.java
来自「一个不错的cache」· Java 代码 · 共 884 行 · 第 1/3 页
JAVA
884 行
/* * Copyright (c) 2002-2003 by OpenSymphony * All rights reserved. */package com.opensymphony.oscache.base;import com.opensymphony.oscache.base.algorithm.AbstractConcurrentReadCache;import com.opensymphony.oscache.base.algorithm.LRUCache;import com.opensymphony.oscache.base.algorithm.UnlimitedCache;import com.opensymphony.oscache.base.events.*;import com.opensymphony.oscache.base.persistence.PersistenceListener;import com.opensymphony.oscache.util.FastCronParser;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import java.io.Serializable;import java.text.ParseException;import java.util.*;import javax.swing.event.EventListenerList;/** * Provides an interface to the cache itself. Creating an instance of this class * will create a cache that behaves according to its construction parameters. * The public API provides methods to manage objects in the cache and configure * any cache event listeners. * * @version $Revision: 1.13.2.1 $ * @author <a href="mailto:mike@atlassian.com">Mike Cannon-Brookes</a> * @author <a href="mailto:tgochenour@peregrine.com">Todd Gochenour</a> * @author <a href="mailto:fbeauregard@pyxis-tech.com">Francois Beauregard</a> * @author <a href="mailto:chris@swebtec.com">Chris Miller</a> */public class Cache implements Serializable { /** * An event that origininated from within another event. */ public static final String NESTED_EVENT = "NESTED"; private static transient final Log log = LogFactory.getLog(Cache.class); /** * A list of all registered event listeners for this cache. */ protected EventListenerList listenerList = new EventListenerList(); /** * The actual cache map. This is where the cached objects are held. */ private AbstractConcurrentReadCache cacheMap = null; /** * Date of last complete cache flush. */ private Date flushDateTime = null; /** * A set that holds keys of cache entries that are currently being built. * The cache checks against this map when a stale entry is requested. * If the requested key is in here, we know the entry is currently being * built by another thread and hence we can either block and wait or serve * the stale entry (depending on whether cache blocking is enabled or not). * <p> * We need to isolate these here since the actual CacheEntry * objects may not normally be held in memory at all (eg, if no * memory cache is configured). */ private Map updateStates = new HashMap(); /** * Indicates whether the cache blocks requests until new content has * been generated or just serves stale content instead. */ private boolean blocking = false; /** * Create a new Cache * * @param useMemoryCaching Specify if the memory caching is going to be used * @param unlimitedDiskCache Specify if the disk caching is unlimited * @param overflowPersistence Specify if the persistent cache is used in overflow only mode */ public Cache(boolean useMemoryCaching, boolean unlimitedDiskCache, boolean overflowPersistence) { this(useMemoryCaching, unlimitedDiskCache, overflowPersistence, false, null, 0); } /** * Create a new Cache. * * If a valid algorithm class is specified, it will be used for this cache. * Otherwise if a capacity is specified, it will use LRUCache. * If no algorithm or capacity is specified UnlimitedCache is used. * * @see com.opensymphony.oscache.base.algorithm.LRUCache * @see com.opensymphony.oscache.base.algorithm.UnlimitedCache * @param useMemoryCaching Specify if the memory caching is going to be used * @param unlimitedDiskCache Specify if the disk caching is unlimited * @param overflowPersistence Specify if the persistent cache is used in overflow only mode * @param blocking This parameter takes effect when a cache entry has * just expired and several simultaneous requests try to retrieve it. While * one request is rebuilding the content, the other requests will either * block and wait for the new content (<code>blocking == true</code>) or * instead receive a copy of the stale content so they don't have to wait * (<code>blocking == false</code>). the default is <code>false</code>, * which provides better performance but at the expense of slightly stale * data being served. * @param algorithmClass The class implementing the desired algorithm * @param capacity The capacity */ public Cache(boolean useMemoryCaching, boolean unlimitedDiskCache, boolean overflowPersistence, boolean blocking, String algorithmClass, int capacity) { // Instantiate the algo class if valid if (((algorithmClass != null) && (algorithmClass.length() > 0)) && (capacity > 0)) { try { cacheMap = (AbstractConcurrentReadCache) Class.forName(algorithmClass).newInstance(); cacheMap.setMaxEntries(capacity); } catch (Exception e) { log.error("Invalid class name for cache algorithm class. " + e.toString()); } } if (cacheMap == null) { // If we have a capacity, use LRU cache otherwise use unlimited Cache if (capacity > 0) { cacheMap = new LRUCache(capacity); } else { cacheMap = new UnlimitedCache(); } } cacheMap.setUnlimitedDiskCache(unlimitedDiskCache); cacheMap.setOverflowPersistence(overflowPersistence); cacheMap.setMemoryCaching(useMemoryCaching); this.blocking = blocking; } /** * Allows the capacity of the cache to be altered dynamically. Note that * some cache implementations may choose to ignore this setting (eg the * {@link UnlimitedCache} ignores this call). * * @param capacity the maximum number of items to hold in the cache. */ public void setCapacity(int capacity) { cacheMap.setMaxEntries(capacity); } /** * Checks if the cache was flushed more recently than the CacheEntry provided. * Used to determine whether to refresh the particular CacheEntry. * * @param cacheEntry The cache entry which we're seeing whether to refresh * @return Whether or not the cache has been flushed more recently than this cache entry was updated. */ public boolean isFlushed(CacheEntry cacheEntry) { if (flushDateTime != null) { long lastUpdate = cacheEntry.getLastUpdate(); return (flushDateTime.getTime() >= lastUpdate); } else { return false; } } /** * Retrieve an object from the cache specifying its key. * * @param key Key of the object in the cache. * * @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) throws NeedsRefreshException { return getFromCache(key, CacheEntry.INDEFINITE_EXPIRY, null); } /** * 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}. * * @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) throws NeedsRefreshException { return getFromCache(key, refreshPeriod, null); } /** * 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)) { EntryUpdateState updateState = getUpdateState(key); 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. updateState.startUpdate(); // We put the updateState object back into the updateStates map so // any remaining threads waiting on this cache entry will be notified // once this thread has done its thing (either updated the cache or // cancelled the update). Without this code they'll get left hanging... synchronized (updateStates) { updateStates.put(key, updateState); } 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; } } } // If reload is true then another thread must have successfully rebuilt the cache entry if (reload) {
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?