Guava中Mapmaker简介
1. 简介
MapMaker是 Guava 中的一个构建器类,可以轻松创建线程安全的Map。
Java 已经支持WeakHashMap为键使用弱引用 。但是,没有开箱即用的解决方案可以对值使用相同的值。幸运的是,MapMaker提供了简单的构建器方法来为键和值使用WeakReference。
在本教程中,让我们看看MapMaker如何轻松创建多个Map并使用弱引用。
2. Maven依赖
首先,让我们添加Maven Central 上提供的Google Guava 依赖项:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.0.1-jre</version>
</dependency>
3. 缓存示例
让我们考虑一个服务器为用户维护几个缓存的简单场景:一个会话缓存和一个配置文件缓存。
会话缓存是短暂的,其条目在用户不再活动后变得无效。因此缓存可以在用户对象被垃圾回收后删除用户的条目。
但是,配置文件缓存可以具有更高的生存时间 (TTL)。只有当用户更新他的个人资料时,个人资料缓存中的条目才会失效。
在这种情况下,只有当配置文件对象被垃圾收集时,缓存才能删除该条目。
3.1. 数据结构
让我们创建类来表示这些实体。
我们首先从用户开始:
public class User {
private long id;
private String name;
public User(long id, String name) {
this.id = id;
this.name = name;
}
public long getId() {
return id;
}
public String getName() {
return name;
}
}
然后是会话:
public class Session {
private long id;
public Session(long id) {
this.id = id;
}
public long getId() {
return id;
}
}
最后是简介:
public class Profile {
private long id;
private String type;
public Profile(long id, String type) {
this.id = id;
this.type = type;
}
public long getId() {
return id;
}
public String getName() {
return type;
}
}
3.2. 创建缓存
让我们使用makeMap方法为会话缓存创建一个ConcurrentMap实例:
ConcurrentMap<User, Session> sessionCache = new MapMaker().makeMap();
返回的映射不允许键和值都为空值。
现在,让我们为配置文件缓存创建另一个ConcurrentMap实例:
ConcurrentMap<User, Profile> profileCache = new MapMaker().makeMap();
请注意,我们没有指定缓存的初始容量。因此,MapMaker默认创建容量为 16 的Map。
如果我们愿意,我们可以使用initialCapacity方法修改容量:
ConcurrentMap<User, Profile> profileCache = new MapMaker().initialCapacity(100).makeMap();
3.3. 更改并发级别
MapMaker将并发级别的默认值设置为 4。但是,sessionCache需要支持更多数量的并发更新,而不会出现任何线程争用。
在这里,concurrencyLevel构建器方法来拯救:
ConcurrentMap<User, Session> sessionCache = new MapMaker().concurrencyLevel(10).makeMap();
3.4. 使用弱引用
我们在上面创建的映射对键和值都使用了强引用。因此,即使键和值被垃圾收集,条目仍保留在映射中。我们应该改用弱引用。
对密钥(用户对象)进行垃圾回收后,sessionCache条目无效。所以,让我们对键使用弱引用:
ConcurrentMap<User, Session> sessionCache = new MapMaker().weakKeys().makeMap();
对于profileCache,我们可以对值使用弱引用:
ConcurrentMap<User, Profile> profileCache = new MapMaker().weakValues().makeMap();
当这些引用被垃圾回收时,Guava 保证这些条目不会包含在 map 上的任何后续读取或写入操作中。但是,*size()*方法有时可能不一致,并且可以包含这些条目。
4. MapMaker内部结构
*如果未启用弱引用,MapMaker默认会创建一个ConcurrentHashMap *。**相等检查通过通常的 equals 方法进行。
**如果我们启用弱引用,那么MapMaker会在内部创建一个由一组哈希表表示的自定义Map。**它还具有与ConcurrentHashMap相似的性能特征。
但是,与WeakHashMap的一个重要区别是相等性检查是通过身份(== 和identityHashCode)比较进行的。