Перейти к основному содержимому

Использование MapMaker от Guava

· 4 мин. чтения

1. Введение

MapMaker — это класс-строитель в Guava, который упрощает создание потокобезопасных карт.

Java уже поддерживает WeakHashMap для использования слабых ссылок для ключей. Но нет готового решения, чтобы использовать то же самое для значений. К счастью, MapMaker предоставляет простые методы построения для использования WeakReference как для ключей, так и для значений .

В этом руководстве мы рассмотрим, как MapMaker упрощает создание нескольких карт и использование слабых ссылок.

2. Зависимость от Maven

Прежде всего, добавим зависимость Google Guava , которая доступна на Maven Central :

<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. Создание кешей

Давайте создадим экземпляр ConcurrentMap для кэша сеанса, используя метод makeMap :

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

Возвращаемая карта не допускает нулевых значений как для ключа, так и для значения.

Теперь давайте создадим еще один экземпляр ConcurrentMap для кеша профиля:

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

Обратите внимание, что мы не указали начальную емкость кэшей. Итак, MapMaker по умолчанию создает карту емкостью 16. ``

Если мы хотим, мы можем изменить емкость, используя метод 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 гарантирует, что эти записи не будут включены ни в одну из последующих операций чтения или записи на карте . Однако иногда метод size() может быть несогласованным и может включать эти записи .

4. Внутреннее устройство MapMaker

MapMaker создает ConcurrentHashMap по умолчанию, если слабые ссылки не включены . Проверка на равенство происходит с помощью обычного метода equals.

Если мы включим слабые ссылки, MapMaker создаст внутреннюю карту , представленную набором хеш-таблиц. Он также имеет те же характеристики производительности, что и ConcurrentHashMap .

Однако важным отличием WeakHashMap является то, что проверки на равенство выполняются посредством сравнения удостоверений (== и identityHashCode ).

5. Вывод

В этой короткой статье мы узнали, как использовать класс MapMaker для создания потокобезопасной карты. Мы также увидели, как настроить карту для использования слабых ссылок.

Как всегда, полный исходный код статьи доступен на GitHub .