This is the 10th day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021.

The Guava cache is lightweight and caches the content into runtime memory. Guava caches are a good choice if certain values (such as some configuration tables) are frequently queried in the system and we are willing to consume some memory space to speed up the application and relieve database stress. Since the cache is stored in running memory, we need to ensure that the size of the cache does not exceed the capacity of memory.

To create the cache

We can create Guava cache objects directly without using any CacheLoader:

Cache<String, String> cache = CacheBuilder.newBuilder().build();

cache.put("hello"."world");
System.out.println(cache.getIfPresent("hello")); // world
Copy the code

The key value is case sensitive, so using cache.getifPresent (“HELLO”) will return null.

Let’s see how to create a cache object using CacheLoader:

CacheLoader<String, String> loader = new CacheLoader<String, String>() {
    @Override
    public String load(String key) {
        returnsayHello(key); }}; LoadingCache<String, String> cache = CacheBuilder.newBuilder().build(loader); String ird = cache.getUnchecked("ird");
System.out.println(ird); // hello ird.private String sayHello(String key) {
    return String.format("hello %s", key);
}
Copy the code

The getUnchecked method computes the value through the CacheLoader when it is not present and stores it in the cache.

Expulsion mechanism

We can limit the size of the cache by defining mechanisms for caching expulsion.

Limit number of caches

We can limit the number of cached entries by maximumSize:

Cache<String, String> cache = CacheBuilder.newBuilder().maximumSize(3).build();

cache.put("k1"."v1");
cache.put("k2"."v2");
cache.put("k3"."v3");
cache.put("k4"."v4");

System.out.println(cache.size()); / / 3
System.out.println(cache.getIfPresent("k1")); // null
System.out.println(cache.asMap()); // {k3=v3, k4=v4, k2=v2}
Copy the code

We limit storage to three values, so k4’s storage drives out the earliest k1, similar to FIFO.

Limiting the cache size

We can customize the weight function to limit the size of the cache:

Weigher<String, String> weigher = (key, value) -> value.length();
Cache<String, String> cache = CacheBuilder.newBuilder().maximumWeight(15).weigher(weigher).build();

cache.put("k1"."11111");
cache.put("k2"."4566");
cache.put("k3"."35673");
cache.put("k4"."636");
cache.put("k5"."555255");

System.out.println(cache.size()); / / 3
System.out.println(cache.getIfPresent("k1")); // null
System.out.println(cache.asMap()); // {k3=35673, k5=555255, k4=636}
Copy the code

In the example above, we specify the maximum size of the cache with maximumWeight(15), which is the length of the value. The value length of k3, k4, and k5 adds up to 13, so k1 and k2 can’t be stored anymore and are expelled.

Setting the cache time

We can set the valid time of the cache and the active time of the cache.

Set the cache active time to 2s:

Cache<String, String> cache = CacheBuilder.newBuilder().expireAfterAccess(2, TimeUnit.SECONDS).build();

cache.put("k1"."v1");
cache.put("k2"."v2");

cache.getIfPresent("k1");
TimeUnit.SECONDS.sleep(1);
cache.getIfPresent("k1");
TimeUnit.SECONDS.sleep(1);

System.out.println(cache.getIfPresent("k1")); // v1
System.out.println(cache.getIfPresent("k2")); // null
System.out.println(cache.size()); / / 1
System.out.println(cache.asMap()); // {k1=v1}
Copy the code

In the code above, we get the value of k1 from cache.getifPresent (“k1”) and block the thread for 1 second, so k1 and k2 are valid for about 1 second. And then it gets the value of k1, so k1 is still valid for 2 seconds, k2 is 1 second, and after blocking the thread again for 1 second, K1 is valid for 1 second, k2 is dead. The printout is as expected.

Set the duration of the cache to 2s:

 Cache<String, String> cache = CacheBuilder.newBuilder().expireAfterWrite(2, TimeUnit.SECONDS).build();

cache.put("k1"."v1");
cache.put("k2"."v2");

cache.getIfPresent("k1");
TimeUnit.SECONDS.sleep(1);
cache.getIfPresent("k1");
TimeUnit.SECONDS.sleep(1);

System.out.println(cache.getIfPresent("k1")); // null
System.out.println(cache.getIfPresent("k2")); // null
System.out.println(cache.size()); / / 0
System.out.println(cache.asMap()); / / {}
Copy the code

Because we set the cache validity time to 2 seconds, all caches expire after 2 seconds, regardless of how many caches were fetched during that time.

weakKeys&softValues

By default, Guava caches key values with strong references. We can use weakKeys and softValues to make the key values weak references so that the garbage collector will work if necessary:

Cache<String, String> cache = CacheBuilder.newBuilder().weakKeys().softValues().build();
Copy the code

Refresh the cache

You can set the automatic cache refresh interval through refreshAfterWrite, or you can manually refresh the cache by calling the refresh method directly:

Cache<String, String> cache = CacheBuilder.newBuilder().refreshAfterWrite(1,TimeUnit.SECONDS).build();
Copy the code

Adding multiple caches

You can add multiple caches at once with putAll:

Cache<String, String> cache = CacheBuilder.newBuilder().build();

Map<String, String> map = Maps.newHashMap();
map.put("k1"."v1");
map.put("k2"."v2");
map.put("k3"."v3");

cache.putAll(map);
System.out.println(cache.size()); / / 3
System.out.println(cache.asMap()); // {k3=v3, k1=v1, k2=v2}
Copy the code

Delete the cache

The cache.invalidate (key) method removes the Cache using the key:

Cache<String, String> cache = CacheBuilder.newBuilder().build();

Map<String, String> map = Maps.newHashMap();
map.put("k1"."v1");
cache.putAll(map);

System.out.println(cache.asMap()); // {k1=v1}
cache.invalidate("k1");
System.out.println(cache.asMap()); / / {}
Copy the code

InvalidateAll (keys) deletes multiple caches at once, or cache.invalidateall () deletes all caches.

We can also add listeners to delete events:

RemovalListener<String, String> listener
                = notification -> System.out.println("Deletion event detected, key=" + notification.getKey() + ",value=" + notification.getValue());

Cache<String, String> cache = CacheBuilder.newBuilder().removalListener(listener).build();
cache.put("k1"."v1");

cache.invalidate("k1"); Key =k1, value=v1
Copy the code

Add and delete

Simply encapsulate a Guava caching utility class:

public class GuavaCacheUtil {
    private static Logger logger = LoggerFactory.getLogger(GuavaCacheUtil.class);

    private static Cache<String, String> cache;

    static {
        RemovalListener<String, String> listener
                = n -> logger.info(Delete event detected, key={}, value={}, n.getKey(), n.getValue());
        cache = CacheBuilder.newBuilder()
                .removalListener(listener).build();
    }

    /** * Add cache **@paramThe key key *@paramThe value value * /
    public void put(String key, String value) {
        if(StringUtils.isNotBlank(key) && StringUtils.isNotBlank(value)) { cache.put(key, value); }}/** * Add cache ** in batches@paramMap key,value set */
    public void putAll(Map<String, String> map) {
        cache.putAll(map);
    }

    /** * delete cache **@paramThe key key * /
    public void remove(String key) {
        if(StringUtils.isNotBlank(key)) { cache.invalidate(key); }}/** * Delete cache ** in batches@paramKeys key set */
    public void remove(List<String> keys) {
        if(CollectionUtils.isNotEmpty(keys)) { cache.invalidateAll(keys); }}/** * clear the cache */
    public void removeAll(a) {
        cache.invalidateAll();
    }

    /** * get cache **@paramThe key key *@returnValue * /
    public String get(String key) {
        return StringUtils.isNotBlank(key) ? cache.getIfPresent(key) : null;
    }

    /** * Get cache ** in batches@paramKeys set *@returnValues set * /
    public ImmutableMap<String, String> get(List<String> keys) {
        return CollectionUtils.isNotEmpty(keys) ? cache.getAllPresent(keys) : null; }}Copy the code