The cache

In a program, a cache is a high-speed data storage layer that stores a subset of the data, often ephemeral, so that it can be requested again at a later date faster than the primary storage location where the data is accessed. Caching enables efficient reuse of previously retrieved or computed data.

Why cache

scenario

In Java applications, for data that is frequently accessed and seldom updated, the usual solution is to add such data to the cache. Compared with reading data from the database, the read cache efficiency is greatly improved.

In the cluster environment, the commonly used distributed cache includes Redis and Memcached. However, in some service scenarios, it may not be necessary to set up a complex distributed cache system. In a stand-alone environment, internal cache (LocalCache) is usually preferred.

plan

  • Developed based on JSR107 specification
  • Implement data caching based on ConcurrentHashMap

JSR107 specification objectives

  • Provides the ability for applications to cache Java objects.
  • Defines a set of general caching concepts and tools.
  • Minimize the learning cost of using caching for developers.
  • Maximize portability of applications using different caching implementations.
  • Support for in-process and distributed caching implementations.

Core concepts of the JSR107 specification

  • Java Caching defines five core interfaces, namely, CachingProvider, CacheManager, Cache, Entry, and Expiry.
  • CachingProvider defines how to create, configure, obtain, manage, and control multiple CacheManager. An application can access multiple CachingProviders at run time.
  • CacheManager defines the creation, configuration, acquisition, management, and control of multiple uniquely named caches in the context of -Cachemanager. A CacheManager is owned by only one CachingProvider.
  • A Cache is a map-like data structure that temporarily stores key-indexed values. A Cache is owned by only one CacheManager.
  • Entry is a key-value pair stored in the Cache.
  • Expiry Duration Each entry stored in the Cache has a defined Expiry Duration. Once this time is exceeded, the entry is in an expired state. Once expired, entries are inaccessible, updated, and deleted. The expiration date of a cache can be set using ExpiryPolicy.

Small example

Use Map to implement a simple caching function

MapCacheDemo.java

package me.xueyao.cache.java;

import java.lang.ref.SoftReference;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;


/ * * *@authorSimon * implements a simple caching feature with map */
public class MapCacheDemo {

    /** * Use ConcurrentHashMap, thread-safe requirements. * I use SoftReference  as a mapping value because a SoftReference guarantees that the referenced Object will be deleted if memory is missing before OutOfMemory is thrown. * In the constructor, I create a daemon thread that scans and cleans up expired objects every 5 seconds. * /
    private static final int CLEAN_UP_PERIOD_IN_SEC = 5;

    private final ConcurrentHashMap<String, SoftReference<CacheObject>> cache = new ConcurrentHashMap<>();

    public MapCacheDemo(a) {
        Thread cleanerThread = new Thread(() -> {
            while(! Thread.currentThread().isInterrupted()) {try {
                    Thread.sleep(CLEAN_UP_PERIOD_IN_SEC * 1000);
                    cache.entrySet().removeIf(entry ->
                            Optional.ofNullable(entry.getValue())
                                    .map(SoftReference::get)
                                    .map(CacheObject::isExpired)
                                    .orElse(false));
                } catch(InterruptedException e) { Thread.currentThread().interrupt(); }}}); cleanerThread.setDaemon(true);
        cleanerThread.start();
    }

    public void add(String key, Object value, long periodInMillis) {
        if (key == null) {
            return;
        }
        if (value == null) {
            cache.remove(key);
        } else {
            long expiryTime = System.currentTimeMillis() + periodInMillis;
            cache.put(key, new SoftReference<>(newCacheObject(value, expiryTime))); }}public void remove(String key) {
        cache.remove(key);
    }

    public Object get(String key) {
        returnOptional.ofNullable(cache.get(key)).map(SoftReference::get).filter(cacheObject -> ! cacheObject.isExpired()).map(CacheObject::getValue).orElse(null);
    }

    public void clear(a) {
        cache.clear();
    }

    public long size(a) {
        returncache.entrySet().stream().filter(entry -> Optional.ofNullable(entry.getValue()).map(SoftReference::get).map(cacheObject - >! cacheObject.isExpired()).orElse(false)).count();
    }

    /** * Cache object value */
    private static class CacheObject {
        private Object value;
        private long expiryTime;

        private CacheObject(Object value, long expiryTime) {
            this.value = value;
            this.expiryTime = expiryTime;
        }

        boolean isExpired(a) {
            return System.currentTimeMillis() > expiryTime;
        }

        public Object getValue(a) {
            return value;
        }

        public void setValue(Object value) {
            this.value = value; }}}Copy the code

Code test class mapCacheDemotests.java

package me.xueyao.cache.java;

public class MapCacheDemoTests {
    public static void main(String[] args) throws InterruptedException {
        MapCacheDemo mapCacheDemo = new MapCacheDemo();
        mapCacheDemo.add("uid_10001"."{1}".5 * 1000);
        mapCacheDemo.add("uid_10002"."{2}".5 * 1000);
        mapCacheDemo.add("uid_10003"."{3}".5 * 1000);
        System.out.println("Fetch value from cache :" + mapCacheDemo.get("uid_10001"));
        Thread.sleep(5000L);
        System.out.println("Five seconds later.");
        System.out.println("Fetch value from cache :" + mapCacheDemo.get("uid_10001"));
        // Data is automatically cleared after 5 seconds ~}}Copy the code