Location>code7788 >text

Implementation of a thread-safe caching tool

Popularity:704 ℃/2025-01-22 00:41:44

Preface

In a multi-threaded environment, caching is a common performance optimization method. However, it is not easy to implement a thread-safe cache, especially in high-concurrency scenarios. How to avoid repeated calculations and ensure data consistency is a challenge.
Recently, when I was reading "Java Concurrent Programming in Practice", the book mentioned a thread-safe cache implementation method based on ConcurrentHashMap and FutureTask. I will share and record it today.

Implementation background

In high-concurrency scenarios, the core role of cache is to avoid repeated calculations. For example, a certain calculation task is very time-consuming. If multiple threads request the same data at the same time, we hope to only calculate it once and use the cached results directly for subsequent requests.
However, implementing such a caching tool needs to consider the following issues:

Thread safety: Multiple threads may access the cache at the same time, how to avoid race conditions?
Avoid double calculations: How to ensure that the same calculation task is only performed once?
Exception handling: If a computing task throws an exception, how to clean the cache and notify the caller?

Implement code

public interface Computable<A, V> {
    V compute(A arg) throws InterruptedException;
}
import ;
 import .*;

 public class CacheUtils<A, V> implements Computable<A, V> {
     private final Map<A, Future<V>> cache = new ConcurrentHashMap<>();
     private final Computable<A, V> computable;

     public CacheUtils(Computable<A, V> computable) {
          = computable;
     }

     @Override
     public V compute(A arg) throws InterruptedException {
         while (true) {
             Future<V> future = (arg);
             if (future == null) {
                 Callable<V> eval = () -> (arg);
                 FutureTask<V> futureTask = new FutureTask<>(eval);
                 future = (arg, futureTask);
                 if (future == null) {
                     future = futureTask;
                     ();
                 }
             }
             try {
                 return ();
             } catch (CancellationException e) {
                 // If the task is canceled, remove the Future from the cache
                 (arg, future);
             } catch (ExecutionException e) {
                 // If the calculation task throws an exception, remove the Future in the cache and throw the exception
                 (arg, future);
                 throw new RuntimeException("Computation failed for argument: " + arg, ());
             }
         }
     }
 }

Implementation idea description

useConcurrentHashMapmiddleputIfAbsentmethod to ensure that only one thread can transfer theFutureTaskPut it into the cache to avoid repeated calculations of the same task; when an exception occurs in the calculation task, an error can be thrown immediately/the cached result can be cancelled.