Contents

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)比较进行的。