缓存的放置时间和删除时间是指缓存中存储的数据的生命周期。这两个时间点非常重要,因为它们决定了缓存数据的有效期和何时应该从缓存中删除。
-
缓存的放置时间(Cache Put Time):这是指数据首次放入缓存的时间点。当数据被放入缓存时,通常会记录下它的放置时间。这个时间点通常用于计算缓存数据的有效期,以便在数据过期后进行更新或重新获取。
-
缓存的删除时间(Cache Expiry Time):这是指缓存数据的有效期截止时间点。缓存数据在这个时间点之后被认为过期,应该从缓存中删除或刷新。删除时间通常是根据放置时间和缓存策略来计算的,不同的缓存策略(如LRU、TTL等)会影响删除时间的确定。
以下是一些常见的缓存管理策略和相关概念:
-
Time-to-Live(TTL):TTL 是指缓存数据被放置后允许存在的时间长度。一旦TTL时间到期,缓存数据将被认为过期,应该被删除。TTL通常与缓存的放置时间相关,例如,一个缓存项可以有一个TTL为60秒,表示数据将在60秒后过期。
-
LRU(Least Recently Used):LRU是一种基于访问顺序的缓存策略。当缓存空间不足时,系统会删除最近最少使用的缓存项。LRU不关心缓存项的时间戳,只关注访问的频率和顺序。
-
LFU(Least Frequently Used):LFU是一种基于访问频率的缓存策略。它会删除访问频率最低的缓存项,无论访问时间是何时。
-
Cache Eviction(缓存淘汰):这是指根据缓存策略从缓存中删除过期或不再需要的数据项的过程。淘汰过程根据策略和时间来计算缓存项的删除时间。
具体的缓存管理实现和删除时间的计算方式取决于使用的缓存库或框架。一般来说,你可以配置缓存的TTL或使用默认的淘汰策略来管理缓存中数据的生命周期,以确保缓存数据的有效性和性能。
当涉及到Java语言中的缓存管理时,通常会使用各种库和框架,最常见的包括:
-
Java Caching API (javax.cache):Java Caching API 是 Java SE 8 的一部分,提供了一种标准的缓存管理方式。它定义了一套缓存管理接口和注解,允许开发人员在应用程序中使用不同的缓存提供程序,如Ehcache、Caffeine等。使用Java Caching API,你可以轻松地创建、配置和管理缓存,以及设置缓存的过期时间。
-
Ehcache:Ehcache 是一个广泛使用的开源缓存库,它提供了灵活的缓存管理功能,包括缓存的放置、过期策略、磁盘持久化等。你可以使用 Ehcache 来创建本地内存缓存或分布式缓存。
-
Caffeine:Caffeine 是一个高性能的本地内存缓存库,特别适用于需要快速访问和低延迟的应用程序。它提供了各种缓存策略,包括定时过期、基于大小的过期等。
-
Guava Cache:Guava Cache 是 Google Guava 库的一部分,提供了简单而强大的本地内存缓存实现。它支持缓存的最大大小、定时过期、基于引用的缓存、监听器等功能。
下面是一个使用Guava Cache的Java示例,演示了如何创建一个本地内存缓存并设置缓存的过期时间:
java
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import java.util.concurrent.TimeUnit;
public class CacheExample {
public static void main(String[] args) {
// 创建一个Guava Cache实例
Cache<String, String> cache = CacheBuilder.newBuilder()
.maximumSize(100) // 设置缓存最大大小
.expireAfterWrite(10, TimeUnit.MINUTES) // 设置缓存项在写入后10分钟过期
.build();
// 向缓存中放置数据
cache.put("key1", "value1");
// 从缓存中获取数据
String value = cache.getIfPresent("key1");
System.out.println("Value for key1: " + value);
// 等待一段时间,以使缓存项过期
try {
Thread.sleep(60000); // 等待1分钟
} catch (InterruptedException e) {
e.printStackTrace();
}
// 尝试获取过期的数据
value = cache.getIfPresent("key1");
System.out.println("Value for key1 after expiration: " + value);
}
}
上面的Java示例演示了如何使用Guava Cache创建一个本地内存缓存,并设置缓存项的过期时间。让我们来分析示例中的操作和结果:
-
创建缓存实例 :通过使用
CacheBuilder
类,我们创建了一个Guava Cache实例,设置了最大缓存大小为100个项,并且定义了缓存项的过期时间为10分钟。这意味着缓存中的项在10分钟后会过期。 -
放置数据 :我们使用
put
方法将一个键值对("key1", "value1")放置到缓存中。 -
获取数据 :我们使用
getIfPresent
方法从缓存中获取键为"key1"的值,并打印出来。在这个示例中,由于数据被刚刚放置到缓存中,所以我们可以成功获取到值,输出为"Value for key1: value1"。 -
等待过期:接下来,我们通过让程序休眠1分钟,模拟了过去一段时间。在这1分钟内,缓存中的数据将保持不变。
-
过期数据:在休眠结束后,我们再次尝试获取键为"key1"的值。这一次,由于缓存项的过期时间已经达到,我们无法再获取到它。因此,输出为"Value for key1 after expiration: null",表示数据已经过期并且不再可用。
这个示例演示了如何使用Guava Cache管理本地内存缓存,并设置缓存项的过期时间。这对于需要控制数据存储在内存中的时间以减少内存占用或确保数据的新鲜性非常有用。不同的应用场景可能需要不同的缓存策略,Guava Cache以及其他缓存库提供了丰富的选项来满足这些需求。
在这个示例中,我们使用了Guava Cache创建了一个本地内存缓存,设置了最大大小和缓存项的过期时间。我们放置了一个键值对到缓存中,然后在一段时间后尝试获取它,验证了过期策略的功能。
不同的缓存库和框架提供了各种各样的功能和配置选项,可以根据应用程序的需求选择适合的缓存解决方案。
三级缓存:CreateBeanInstance之后:addSingletonFactory
二级缓存:第一次从三级缓存确定对象是代理对象还是不同对象的时候,同时删除三级缓存getSingleton
一级缓存:生成完整对象之后放到一级缓存,删除二三级缓存:addSingleton
以下是一个使用Java示例代码,演示如何在Java中使用 java.util.HashMap
实现具有放置时间和删除时间的缓存概念。我们将自己管理缓存项的过期时间。
java
import java.util.HashMap;
import java.util.Map;
public class Cache<K, V> {
private final Map<K, CacheEntry<V>> cache = new HashMap<>();
private final long defaultTtl; // 默认的过期时间(以毫秒为单位)
public Cache(long defaultTtl) {
this.defaultTtl = defaultTtl;
}
public void put(K key, V value) {
put(key, value, defaultTtl);
}
public void put(K key, V value, long ttl) {
long expirationTime = System.currentTimeMillis() + ttl;
cache.put(key, new CacheEntry<>(value, expirationTime));
}
public V get(K key) {
CacheEntry<V> entry = cache.get(key);
if (entry != null && entry.isNotExpired()) {
return entry.getValue();
} else {
cache.remove(key);
return null;
}
}
private static class CacheEntry<V> {
private final V value;
private final long expirationTime;
CacheEntry(V value, long expirationTime) {
this.value = value;
this.expirationTime = expirationTime;
}
boolean isNotExpired() {
return System.currentTimeMillis() <= expirationTime;
}
V getValue() {
return value;
}
}
public static void main(String[] args) {
Cache<String, String> cache = new Cache<>(60000); // 设置默认过期时间为60秒
cache.put("key1", "value1");
cache.put("key2", "value2", 30000); // 自定义过期时间为30秒
System.out.println("Value for key1: " + cache.get("key1")); // 输出 "value1"
System.out.println("Value for key2: " + cache.get("key2")); // 输出 "value2"
// 等待一段时间,让部分数据过期
try {
Thread.sleep(35000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Value for key1: " + cache.get("key1")); // 输出 null,数据已过期
System.out.println("Value for key2: " + cache.get("key2")); // 输出 null,数据已过期
}
}
在上述示例中,我们创建了一个简单的泛型缓存类 Cache
,它允许存储键值对并为每个缓存项设置过期时间。我们使用 java.util.HashMap
存储缓存项,并在获取数据时检查过期时间以确保数据有效性。当数据过期时,我们从缓存中删除它。最后,我们在 main
方法中演示了如何使用这个自定义缓存类。
当你运行上述Java示例代码时,根据缓存项的过期时间和休眠时间,你将获得以下输出结果:
java
Value for key1: value1
Value for key2: value2
Value for key1: null
Value for key2: null
解释一下这些输出结果:
-
首先,我们将"key1"和"key2"存储在缓存中,并设置了它们的过期时间。然后,我们通过
cache.get("key1")
和cache.get("key2")
获取它们的值,因为它们都在有效期内,所以返回了相应的值。 -
然后,我们休眠了约35秒,让部分数据过期。这会导致"key1"和"key2"的缓存项过期。
-
最后,我们再次尝试获取"key1"和"key2"的值,由于它们的缓存项已过期,所以返回了
null
。这表明数据已不再可用。