Using Guava’s MapMaker – 使用Guava’的MapMaker

最后修改: 2020年 4月 8日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.绪论

MapMaker is a builder class in Guava that makes it easy to create thread-safe maps.

MapMaker是Guava中的一个构建器类,它使创建线程安全的地图变得容易。

Java already supports WeakHashMap to use Weak References for the keys. But, there is no out-of-the-box solution to use the same for the values. Luckily, MapMaker provides simple builder methods to use WeakReference for both the keys and the values.

Java已经支持WeakHashMap为键使用Weak References。但是,并没有开箱即用的解决方案来为值使用同样的方法。幸运的是,MapMaker提供了简单的构建方法来对键和值使用WeakReference

In this tutorial, let’s see how MapMaker makes it easy to create multiple maps and to use weak references.

在本教程中,让我们看看MapMaker如何使创建多个地图和使用弱引用变得容易。

2. Maven Dependency

2.Maven的依赖性

First of all, let’s add the Google Guava dependency, which is available on Maven Central:

首先,让我们添加Google Guava依赖,该依赖可在Maven中心获得。

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>31.0.1-jre</version>
</dependency>

3. A Caching Example

3.缓存实例

Let’s consider a simple scenario of a server maintaining a couple of caches for the users: a session cache and a profile cache.

让我们考虑一个简单的场景:服务器为用户维护几个缓存:一个会话缓存和一个配置文件缓存。

The session cache is short-lived with its entries becoming invalid after the user is no longer active. So the cache can remove the entry for the user after the user object is garbage-collected.

会话缓存是短暂的,它的条目在用户不再活动后就会失效。所以缓存可以在用户对象被垃圾收集后删除用户的条目。

The profile cache, however, can have a higher time-to-live (TTL). The entries in the profile cache become invalid only when the user updates his profile.

然而,档案缓存可以有一个较高的生存时间(TTL)。配置文件缓存中的条目只有在用户更新其配置文件时才会失效。

In this case, the cache can remove the entry only when the profile object is garbage-collected.

在这种情况下,只有当配置文件对象被垃圾收集时,缓存才能删除该条目。

3.1. Data Structures

3.1.数据结构

Let’s create classes to represent these entities.

让我们创建类来表示这些实体。

We’ll start first with the user:

我们首先从用户开始。

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;
    }
}

Then the session:

然后是会议。

public class Session {
    private long id;

    public Session(long id) {
        this.id = id;
    }

    public long getId() {
        return id;
    }
}

And finally the profile:

最后是简介。

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. Creating the Caches

3.2.创建缓存

Let’s create an instance of ConcurrentMap for the session cache using the makeMap method:

让我们使用makeMap方法为会话缓存创建一个ConcurrentMap的实例。

ConcurrentMap<User, Session> sessionCache = new MapMaker().makeMap();

The returned map does not allow null values for both the key and the value.

返回的地图不允许键和值都为空值。

Now, let’s create another instance of ConcurrentMap for the profile cache:

现在,让我们为配置文件缓存创建另一个ConcurrentMap的实例。

ConcurrentMap<User, Profile> profileCache = new MapMaker().makeMap();

Notice that we have not specified the initial capacity for the caches. So, MapMaker creates a map of capacity 16 by default.

注意,我们没有指定缓存的初始容量。所以,MapMaker默认创建了一个容量为16的地图。

If we want to we can modify the capacity using the initialCapacity method:

如果我们想,我们可以使用initialCapacity方法修改容量。

ConcurrentMap<User, Profile> profileCache = new MapMaker().initialCapacity(100).makeMap();

3.3. Changing the Concurrency Level

3.3.改变并发级别

MapMaker sets the default value for the concurrency level to 4. However, the sessionCache needs to support a higher number of concurrent updates without any thread contention.

MapMaker设置并发级别的默认值为4。然而,sessionCache需要支持更多的并发更新,而不需要任何线程争用。

Here, the concurrencyLevel builder method comes to the rescue:

在这里,concurrencyLevelbuilder方法来拯救。

ConcurrentMap<User, Session> sessionCache = new MapMaker().concurrencyLevel(10).makeMap();

3.4. Using Weak References

3.4.使用弱的引用

The maps we created above use strong references for both the keys and values. So, the entries stay in the map even if the keys and the values are garbage-collected. We should use weak references instead.

我们上面创建的地图对键和值都使用了强引用。因此,即使键和值被垃圾收集,条目也会留在地图中。我们应该使用弱引用来代替。

A sessionCache entry is invalid after the key (the user object) is garbage-collected. So, let’s use weak references for the keys:

一个sessionCache条目在键(用户对象)被垃圾收集后是无效的。所以,让我们用弱引用来做键。

ConcurrentMap<User, Session> sessionCache = new MapMaker().weakKeys().makeMap();

For the profileCache, we can use weak references for the values:

对于profileCache,我们可以使用弱引用来获取值。

ConcurrentMap<User, Profile> profileCache = new MapMaker().weakValues().makeMap();

When these references are garbage-collected, Guava guarantees that these entries will not be included in any of the subsequent read or write operations on the map. However, the size() method might sometimes be inconsistent and can include these entries.

当这些引用被垃圾收集时,Guava保证这些entries 不会被包含在地图的任何后续读或写操作中。然而,size()方法有时可能不一致,可能包括这些entries

4. MapMaker Internals

4.MapMaker内部结构

MapMaker creates a ConcurrentHashMap by default if weak references are not enabled. The equality checks happen via the usual equals method.

MapMaker 如果没有启用弱引用,默认会创建一个ConcurrentHashMap平等检查是通过通常的等价方法进行的。

If we enable weak references, then MapMaker creates a custom map represented by a set of hash tables internally. It also shares similar performance characteristics as a ConcurrentHashMap.

如果我们启用弱引用,那么MapMaker就会在内部创建一个由一组哈希表代表的自定义地图。它也具有与ConcurrentHashMap类似的性能特征。

However, an important difference with WeakHashMap is that the equality checks happen via the identity (== and identityHashCode) comparisons.

然而,与WeakHashMap的一个重要区别是,平等检查是通过身份(==和identityHashCode)比较进行的。

5. Conclusion

5.总结

In this short article, we learned how to use the MapMaker class to create a thread-safe map. We also saw how to customize the map to use weak references.

在这篇短文中,我们学习了如何使用MapMaker类来创建一个线程安全的地图。我们还看到了如何定制地图以使用弱引用。

As always, the full source code of the article is available over on GitHub.

一如既往,文章的完整源代码可在GitHub上获得over。