Implementing a thread-safe cache using ConcurrentHashMap and HashMap in Java

When building concurrent applications in Java, it is essential to handle thread safety to avoid data inconsistency and race conditions. One common use case is implementing a cache that can be accessed by multiple threads simultaneously. In this blog post, we will explore how to implement a thread-safe cache using both ConcurrentHashMap and HashMap in Java.

Table of Contents

ConcurrentHashMap

ConcurrentHashMap is a built-in class in Java that provides thread-safe operations for concurrent access. It implements a variant of hash table that allows multiple threads to read and write simultaneously.

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class ThreadSafeCache {
    private Map<String, Object> cache = new ConcurrentHashMap<>();

    public Object get(String key) {
        return cache.get(key);
    }

    public void put(String key, Object value) {
        cache.put(key, value);
    }

    // Other methods...
}

In the above example, we create a class ThreadSafeCache which internally uses ConcurrentHashMap for storing key-value pairs. The get() and put() methods simply delegate the operations to the underlying ConcurrentHashMap.

The advantage of using ConcurrentHashMap is that it handles concurrent access out-of-the-box, ensuring thread safety without the need for external synchronization.

HashMap with Synchronization

If you are not using Java 8 or newer, which provides ConcurrentHashMap out-of-the-box, you can still achieve thread safety with a HashMap by adding synchronization manually.

import java.util.HashMap;
import java.util.Map;

public class ThreadSafeCache {
    private Map<String, Object> cache = new HashMap<>();

    public synchronized Object get(String key) {
        return cache.get(key);
    }

    public synchronized void put(String key, Object value) {
        cache.put(key, value);
    }

    // Other methods...
}

In the above example, we have added the synchronized keyword to the get() and put() methods. This ensures that only one thread can access these methods at a time, making the ThreadSafeCache class thread-safe. However, this approach may introduce performance overhead due to locking.

Choosing the Right Implementation

When deciding between ConcurrentHashMap and synchronized HashMap, consider the following factors:

Additionally, you can benchmark both implementations to determine which one suits your specific use case.

Conclusion

In this blog post, we explored how to implement a thread-safe cache using both ConcurrentHashMap and synchronized HashMap in Java. ConcurrentHashMap is the recommended approach for achieving thread safety when concurrent access is crucial. However, if you are using an older Java version, it is still possible to implement thread safety by manually synchronizing a HashMap.