The paper

We run into caching scenarios with expiration times in our work. However, using Redis is too heavy, after all, the cache data is small, enough to put in memory. Hutools provides TimedCache time caching tool to implement this scenario. The following uses this component and optimizes and upgrades the tool class for the work scenario.

Maven rely on

<dependency> <groupId>cn. Hutool </groupId> <artifactId>hutool-all</artifactId> <version>5.4.6</version> </dependency> < the dependency > < groupId > com. Google. Guava < / groupId > < artifactId > guava < / artifactId > < version > 30.1.1 - jre < / version > </dependency>Copy the code

Simple to use

So without further ado, go to the code.

import cn.hutool.cache.CacheUtil;
import cn.hutool.cache.impl.TimedCache;
import cn.hutool.core.thread.ThreadUtil;

/ * *@Author huyi @Date 2021/10/12 17:00 @Description: * /
public class TimedCacheUtils {
  private static final TimedCache<String, String> TIMED_CACHE = CacheUtil.newTimedCache(5000);

  static {
    /** Check expiration every 5ms */
    TIMED_CACHE.schedulePrune(5);
  }

  /** * stores key-value pairs to provide elapsed time **@param key
   * @param value
   * @param timeout
   */
  public static void put(String key, String value, Long timeout) {
    /** Set the elapsed time */
    TIMED_CACHE.put(key, value, timeout);
  }

  /** * The elapsed time is refreshed every time the cache is regotten@param key
   * @return* /
  public static String get(String key) {
    return TIMED_CACHE.get(key);
  }

  public static void main(String[] args) {
    put("haha"."1".3000L);
    ThreadUtil.sleep(2000);
    // if (TIMED_CACHE.containsKey("haha")) {
    // System.out.println("aa");
    / /}
    System.out.println("1st result :" + get("haha"));
    ThreadUtil.sleep(2000);
    System.out.println("Second result :" + get("haha"));
    ThreadUtil.sleep(5000);
    System.out.println("Third result :" + get("haha"));
    // Cancel scheduled cleanupTIMED_CACHE.cancelPruneSchedule(); }}Copy the code

First let’s look at the effect of the implementation

Note: * * * *

1. Set the timeout time to 3000 ms, so the first print is in 2 seconds, so you can get the value.

2. Because the first print invokes the GET method and refreshes the expiration time, the value is still available.

3. The third print is 5 seconds later, so it has expired and cannot get the value. Print NULL.

So, you need to know if the cache is still available to use the containsKey method. As follows:

    put("haha"."1".3000L);
    ThreadUtil.sleep(2000);
    if (TIMED_CACHE.containsKey("haha")) {
      System.out.println("1st result: Cache exists");
    }
Println (" first result :" + get("haha")); // system.out. println(" first result :" + get("haha"));
    ThreadUtil.sleep(2000);
    System.out.println("Second result :" + get("haha"));
    ThreadUtil.sleep(5000);
    System.out.println("Third result :" + get("haha"));
    // Cancel scheduled cleanup
    TIMED_CACHE.cancelPruneSchedule();
Copy the code

Tool optimization – Listen for expiration and add callbacks

Using TimedCache, we find that we do not immediately know when the cache expires, and many work scenarios require listening callbacks to the cache. So I updated the tool class.

import cn.hutool.cache.CacheUtil;
import cn.hutool.cache.impl.TimedCache;
import cn.hutool.core.thread.ThreadUtil;
import com.google.common.util.concurrent.*;
import org.checkerframework.checker.nullness.qual.Nullable;
 
import java.text.MessageFormat;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Consumer;
 
/ * *@AuthorThe swordsman Aliang@Date 2021/10/12 10:57 @Description: time caching tool */
public class TimedCacheUtils {
  private static final TimedCache<String, String> TIMED_CACHE = CacheUtil.newTimedCache(5000);
  /** Thread pool */
  private static final ExecutorService executorService = Executors.newCachedThreadPool();
 
  private static final ListeningExecutorService listeningExecutorService =
      MoreExecutors.listeningDecorator(executorService);
  /** Callback method mapping */
  private static ConcurrentHashMap<String, Consumer<String>> callbackMap;
 
  /** * Store key-value pairs, add expiration time, and consume callback **@param key
   * @param timeout
   * @param consumer
   */
  public static void put(String key, String value, Long timeout, Consumer<String> consumer) {
    TIMED_CACHE.put(key, value, timeout);
    addListen(key, consumer);
  }
 
  /** * get the cache value **@param key
   * @return* /
  public static String get(String key) {
    return TIMED_CACHE.get(key);
  }
 
  /** * Delete cache and callback mappings **@param key
   */
  public static void remove(String key) {
    callbackMap.remove(key);
    TIMED_CACHE.remove(key);
  }
 
  /** * Add listener **@param key
   * @param consumer
   */
  public static void addListen(String key, Consumer<String> consumer) {
    ListenableFuture<String> listenableFuture =
        listeningExecutorService.submit(
            () -> {
              while (TIMED_CACHE.containsKey(key)) {
                ThreadUtil.sleep(500);
              }
              return key;
            });
    Futures.addCallback(
        listenableFuture,
        new FutureCallback<String>() {
          @Override
          public void onSuccess(@Nullable String s) {
            consumer.accept(s);
          }
 
          @Override
          public void onFailure(Throwable throwable) {
            throwable.printStackTrace();
          }
        },
        listeningExecutorService);
  }
 
  public static void main(String[] args) {
    put("haha"."1".3000L, x -> System.out.println(MessageFormat.format("[{0}] - Cache disappeared", x)));
    ThreadUtil.sleep(2000);
    System.out.println(get("haha"));
    ThreadUtil.sleep(2000);
    System.out.println(get("haha"));
    ThreadUtil.sleep(5000);
    System.out.println(get("haha"));
    // Close the listening thread poollisteningExecutorService.shutdown(); }}Copy the code

** Result :**

Note: * * * *

1. You can see that the cache is out of date and a callback is made.

conclusion

The specific usage scenarios of the utility classes vary from project to project, as you can see.

If this article helped you, please give it a thumbs up.

My CSDN home page address: Swordsman Aliang’s home page

Learn together and make progress together.