一、pom.xml 依赖项
1 <dependency> 2 <groupId>net.sf.ehcache</groupId> 3 <artifactId>ehcache</artifactId> 4 <version>2.8.3</version> 5 </dependency> 6 7 <dependency> 8 <groupId>org.slf4j</groupId> 9 <artifactId>slf4j-api</artifactId> 10 <version>1.7.7</version> 11 </dependency>
二、ehcache.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 3 <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true" 5 monitoring="autodetect" dynamicConfig="true"> 6 7 8 <diskStore path="java.io.tmpdir" /> 9 10 <defaultCache maxEntriesLocalHeap="10000" eternal="false" 11 timeToIdleSeconds="120" timeToLiveSeconds="120" diskSpoolBufferSizeMB="30" 12 maxEntriesLocalDisk="10000000" diskExpiryThreadIntervalSeconds="120" 13 memoryStoreEvictionPolicy="LRU"> 14 <persistence strategy="localTempSwap" /> 15 </defaultCache> 16 17 <cache name="sampleCache1" maxEntriesLocalHeap="10000" 18 maxEntriesLocalDisk="1000" eternal="false" diskSpoolBufferSizeMB="20" 19 timeToIdleSeconds="300" timeToLiveSeconds="600" 20 memoryStoreEvictionPolicy="LFU" transactionalMode="off"> 21 <persistence strategy="localTempSwap" /> 22 </cache> 23 24 <cache name="sampleCache2" maxEntriesLocalHeap="1000" eternal="true" 25 memoryStoreEvictionPolicy="FIFO" /> 26 27 </ehcache>
三、示例代码
1 package cnblogs.ehcache; 2 3 import net.sf.ehcache.Cache; 4 import net.sf.ehcache.CacheManager; 5 import net.sf.ehcache.Element; 6 7 public class App { 8 public static void main(String[] args) throws InterruptedException { 9 CacheManager manager = CacheManager.create(); 10 11 // 取出所有的cacheName 12 String names[] = manager.getCacheNames(); 13 System.out.println("----all cache names----"); 14 for (int i = 0; i < names.length; i++) { 15 System.out.println(names[i]); 16 } 17 18 System.out.println("----------------------"); 19 // 得到一个cache对象 20 Cache cache1 = manager.getCache(names[0]); 21 22 // 向cache1对象里添加缓存 23 cache1.put(new Element("key1", "values1")); 24 Element element = cache1.get("key1"); 25 26 // 读取缓存 27 System.out.println("key1 = " + element.getObjectValue()); 28 29 // 手动创建一个cache(ehcache里必须有defaultCache存在,"test"可以换成任何值) 30 Cache cache2 = new Cache("test", 1, true, false, 2, 3); 31 manager.addCache(cache2); 32 33 cache2.put(new Element("jimmy", "菩提树下的杨过")); 34 35 // 故意停1.5秒,以验证是否过期 36 Thread.sleep(1500); 37 38 Element eleJimmy = cache2.get("jimmy"); 39 40 //1.5s < 2s 不会过期 41 if (eleJimmy != null) { 42 System.out.println("jimmy = " + eleJimmy.getObjectValue()); 43 } 44 45 //再等上0.5s, 总时长:1.5 + 0.5 >= min(2,3),过期 46 Thread.sleep(500); 47 48 eleJimmy = cache2.get("jimmy"); 49 50 if (eleJimmy != null) { 51 System.out.println("jimmy = " + eleJimmy.getObjectValue()); 52 } 53 54 // 取出一个不存在的缓存项 55 System.out.println("fake = " + cache2.get("fake")); 56 57 manager.shutdown(); 58 } 59 60 }
运行结果:
----all cache names----
sampleCache2
sampleCache1
----------------------
key1 = values1
jimmy = 菩提树下的杨过
fake = null
四、关于timeToLiveSeconds、timeToIdleSeconds
这二个参数直接影响缓存项的过期时间,看文档说明基本上没啥用,直接看net.sf.ehcache.Element源码的片段:
1 /** 2 * The amount of time for the element to live, in seconds. 0 indicates unlimited. 3 */ 4 private volatile int timeToLive = Integer.MIN_VALUE; 5 6 /** 7 * The amount of time for the element to idle, in seconds. 0 indicates unlimited. 8 */ 9 private volatile int timeToIdle = Integer.MIN_VALUE; 10 11 12 /** 13 * Sets time to Live 14 * <P/> 15 * Value must be a positive integer, 0 means infinite time to live. 16 * <P/> 17 * If calling this method with 0 as the parameter, consider using {@link #setEternal(boolean)} 18 * or make sure you also explicitly call {@link #setTimeToIdle(int)}. 19 * 20 * @param timeToLiveSeconds the number of seconds to live 21 */ 22 public void setTimeToLive(final int timeToLiveSeconds) { 23 if (timeToLiveSeconds < 0) { 24 throw new IllegalArgumentException("timeToLive can't be negative"); 25 } 26 this.cacheDefaultLifespan = false; 27 this.timeToLive = timeToLiveSeconds; 28 } 29 30 /** 31 * Sets time to idle 32 * <P/> 33 * Value must be a positive integer, 0 means infinite time to idle. 34 * <P/> 35 * If calling this method with 0 as the parameter, consider using {@link #setEternal(boolean)} 36 * or make sure you also explicitly call {@link #setTimeToLive(int)}. 37 * 38 * @param timeToIdleSeconds the number of seconds to idle 39 */ 40 public void setTimeToIdle(final int timeToIdleSeconds) { 41 if (timeToIdleSeconds < 0) { 42 throw new IllegalArgumentException("timeToIdle can't be negative"); 43 } 44 this.cacheDefaultLifespan = false; 45 this.timeToIdle = timeToIdleSeconds; 46 } 47 48 49 50 /** 51 * An element is expired if the expiration time as given by {@link #getExpirationTime()} is in the past. 52 * 53 * @return true if the Element is expired, otherwise false. If no lifespan has been set for the Element it is 54 * considered not able to expire. 55 * @see #getExpirationTime() 56 */ 57 public boolean isExpired() { 58 if (!isLifespanSet() || isEternal()) { 59 return false; 60 } 61 62 long now = System.currentTimeMillis(); 63 long expirationTime = getExpirationTime(); 64 65 return now > expirationTime; 66 } 67 68 69 /** 70 * An element is expired if the expiration time as given by {@link #getExpirationTime()} is in the past. 71 * <p> 72 * This method in addition propogates the default TTI/TTL values of the supplied cache into this element. 73 * 74 * @param config config to take default parameters from 75 * @return true if the Element is expired, otherwise false. If no lifespan has been set for the Element it is 76 * considered not able to expire. 77 * @see #getExpirationTime() 78 */ 79 public boolean isExpired(CacheConfiguration config) { 80 if (cacheDefaultLifespan) { 81 if (config.isEternal()) { 82 timeToIdle = 0; 83 timeToLive = 0; 84 } else { 85 timeToIdle = TimeUtil.convertTimeToInt(config.getTimeToIdleSeconds()); 86 timeToLive = TimeUtil.convertTimeToInt(config.getTimeToLiveSeconds()); 87 } 88 } 89 return isExpired(); 90 } 91 92 /** 93 * Returns the expiration time based on time to live. If this element also has a time to idle setting, the expiry 94 * time will vary depending on whether the element is accessed. 95 * 96 * @return the time to expiration 97 */ 98 public long getExpirationTime() { 99 if (!isLifespanSet() || isEternal()) { 100 return Long.MAX_VALUE; 101 } 102 103 long expirationTime = 0; 104 long ttlExpiry = creationTime + TimeUtil.toMillis(getTimeToLive()); 105 106 long mostRecentTime = Math.max(creationTime, lastAccessTime); 107 long ttiExpiry = mostRecentTime + TimeUtil.toMillis(getTimeToIdle()); 108 109 if (getTimeToLive() != 0 && (getTimeToIdle() == 0 || lastAccessTime == 0)) { 110 expirationTime = ttlExpiry; 111 } else if (getTimeToLive() == 0) { 112 expirationTime = ttiExpiry; 113 } else { 114 expirationTime = Math.min(ttlExpiry, ttiExpiry); 115 } 116 return expirationTime; 117 } 118 119 /** 120 * @return true if the element is eternal 121 */ 122 public boolean isEternal() { 123 return (0 == timeToIdle) && (0 == timeToLive); 124 } 125 126 127 /** 128 * Sets whether the element is eternal. 129 * 130 * @param eternal 131 */ 132 public void setEternal(final boolean eternal) { 133 if (eternal) { 134 this.cacheDefaultLifespan = false; 135 this.timeToIdle = 0; 136 this.timeToLive = 0; 137 } else if (isEternal()) { 138 this.cacheDefaultLifespan = false; 139 this.timeToIdle = Integer.MIN_VALUE; 140 this.timeToLive = Integer.MIN_VALUE; 141 } 142 } 143 144 /** 145 * Whether any combination of eternal, TTL or TTI has been set. 146 * 147 * @return true if set. 148 */ 149 public boolean isLifespanSet() { 150 return this.timeToIdle != Integer.MIN_VALUE || this.timeToLive != Integer.MIN_VALUE; 151 } 152 153 /** 154 * @return the time to live, in seconds 155 */ 156 public int getTimeToLive() { 157 if (Integer.MIN_VALUE == timeToLive) { 158 return 0; 159 } else { 160 return timeToLive; 161 } 162 } 163 164 /** 165 * @return the time to idle, in seconds 166 */ 167 public int getTimeToIdle() { 168 if (Integer.MIN_VALUE == timeToIdle) { 169 return 0; 170 } else { 171 return timeToIdle; 172 } 173 } 174 175 176 /** 177 * Set the default parameters of this element - those from its enclosing cache. 178 * @param tti TTI in seconds 179 * @param ttl TTL in seconds 180 * @param eternal <code>true</code> if the element is eternal. 181 */ 182 protected void setLifespanDefaults(int tti, int ttl, boolean eternal) { 183 if (eternal) { 184 this.timeToIdle = 0; 185 this.timeToLive = 0; 186 } else if (isEternal()) { 187 this.timeToIdle = Integer.MIN_VALUE; 188 this.timeToLive = Integer.MIN_VALUE; 189 } else { 190 timeToIdle = tti; 191 timeToLive = ttl; 192 } 193 }
结论:
a) timeToIdleSeconds(空闲时间)、timeToLiveSeconds(生存时间)都设置为0时,表示不过期
b) 如果只有timeToLiveSeconds设置>0的值,则Element的过期时间为 timeToLiveSeconds
c) 如果只有timeToIdleSeconds设置>0的值,则Element的过期时间为 (上次访问时间+timeToIdleSeconds),说得更通俗点,上次get过了,现在又想get,若二次get的时间间隔>timeToIdleSeconds,则过期(即:最后一次get出来为null)
d) 如果timeToLiveSeconds、timeToIdleSeconds都有>0的值,则最终过期时间为 b),c)规则综合起来,取二者的最小值
测试1:
1 @Test 2 public void testTimeToIdleSeconds() throws InterruptedException { 3 CacheManager manager = CacheManager.create(); 4 5 Cache myCache = new Cache("MyCache", 1, true, false, 0, 0); // Cache上设置为永不过期 6 manager.addCache(myCache); 7 8 String key = "A"; 9 10 System.out.println("-------------------------"); 11 Element elementPut = new Element(key, "Some Value", 2, 0); // timeToIdleSeconds为2秒 12 13 myCache.put(elementPut);// 放入缓存 14 System.out.println(myCache.get(key));// 取出显示 15 16 Thread.sleep(1500);// 停1.5秒 17 System.out.println(myCache.get(key));// 再次取出 18 19 Thread.sleep(1500);// 停1.5秒 20 System.out.println(myCache.get(key));// 虽然总时间已达3秒,但刚刚被访问过了,所以又可以再"活"2秒,仍然有效 21 22 Thread.sleep(2500);// 停2.5秒 23 System.out.println(myCache.get(key));// 距离上次访问已过2.5s,已经>2s,过期 24 25 }
输出结果
[ key = A, value=Some Value, version=1, hitCount=1, CreationTime = 1407898361782, LastAccessTime = 1407898361787 ]
[ key = A, value=Some Value, version=1, hitCount=2, CreationTime = 1407898361782, LastAccessTime = 1407898363287 ]
[ key = A, value=Some Value, version=1, hitCount=3, CreationTime = 1407898361782, LastAccessTime = 1407898364787 ]
null
测试2:
1 @Test 2 public void testTimeToLiveSeconds() throws InterruptedException { 3 CacheManager manager = CacheManager.create(); 4 5 Cache myCache = new Cache("MyCache", 1, true, false, 0, 0); // Cache上设置为永不过期 6 manager.addCache(myCache); 7 8 String key = "A"; 9 10 System.out.println("-------------------------"); 11 Element elementPut = new Element(key, "Some Value", 0, 2); // timeToLiveSeconds为2秒 12 13 myCache.put(elementPut);// 放入缓存 14 System.out.println(myCache.get(key));// 取出显示 15 16 Thread.sleep(1500);// 停1.5秒 17 System.out.println(myCache.get(key));// 再次取出(1.5s<2s,还"活"着) 18 19 Thread.sleep(1500);// 停1.5秒 20 System.out.println(myCache.get(key));// 总时间已达3s,>2s,已过期) 21 22 }
输出结果
[ key = A, value=Some Value, version=1, hitCount=1, CreationTime = 1407898423291, LastAccessTime = 1407898423296 ]
[ key = A, value=Some Value, version=1, hitCount=2, CreationTime = 1407898423291, LastAccessTime = 1407898424797 ]
null
测试3:
1 @Test 2 public void testTimeToIdleSecondsAndTimeToLiveSeconds() 3 throws InterruptedException { 4 CacheManager manager = CacheManager.create(); 5 6 Cache myCache = new Cache("MyCache", 1, true, false, 0, 0); // Cache上设置为永不过期 7 manager.addCache(myCache); 8 9 String key = "A"; 10 11 System.out.println("-------------------------"); 12 Element elementPut = new Element(key, "Some Value", 2, 5); // timeToIdleSeconds为2秒,timeToLiveSeconds为3秒 13 14 myCache.put(elementPut);// 放入缓存 15 System.out.println(myCache.get(key));// 取出显示 16 17 Thread.sleep(1600);// 停1.6秒 18 System.out.println(myCache.get(key));// 再次取出(1.6s < min(2 ,5),还"活"着) 19 20 Thread.sleep(1600);// 停1.6秒 21 System.out.println(myCache.get(key));// 总时间已达3.2s,< min((1.6+2) ,5),还"活"着) 22 23 Thread.sleep(1600);// 停1.6秒 24 System.out.println(myCache.get(key));// 总时间已达4.8s,< min((3.2+2) ,5),还"活"着) 25 26 Thread.sleep(500);// 停0.5秒 27 System.out.println(myCache.get(key));// 总时间已达4.8+0.5=5.3s,> min((4.8+2) ,5),过期) 28 29 }
输出结果
[ key = A, value=Some Value, version=1, hitCount=1, CreationTime = 1407898480892, LastAccessTime = 1407898480897 ]
[ key = A, value=Some Value, version=1, hitCount=2, CreationTime = 1407898480892, LastAccessTime = 1407898482499 ]
[ key = A, value=Some Value, version=1, hitCount=3, CreationTime = 1407898480892, LastAccessTime = 1407898484099 ]
[ key = A, value=Some Value, version=1, hitCount=4, CreationTime = 1407898480892, LastAccessTime = 1407898485699 ]
null
关于这二个参数的设置,个人建议是:
a) 如果缓存的数据本身不存在更新(比如:一些几乎从来不动的基础数据),只设置timeToIdleSeconds,这样的好处是,如果缓存项一直有人在访问,就永远不会过期,反之,如果没人用,空闲一段时间后,会自动过期,释放资源
b) 如果缓存的数据本身存在定期的更新问题(比如:天气预报之类每隔几小时,db中会更新的数据),可同时设置二个参数,timeToLiveSeconds的值应该要小于db中的更新周期,这样db中的数据变化后,过一段时间就会更新到缓存中