GDT GDT - 7 months ago 12
Java Question

How to implement a cache?

I do not understand what a cache actually is. For example, if I wanted to code a cache, would I need to store data locally using maps.

Answer

Conceptually, using a ConcurrentMap works. In essence, the atomic get/put methods are helpful. However, instead of storing the object directly, create a CacheEntity type of object, such that one uses ConcurrentMap<KeyType, CacheEntity>. The CacheEntity can carry the creation time/last accessed time and the actual object. In the <T> cache.get(KeyType key) method, if one finds the object update the last access time on the CacheEntity object and then return the contained Object value. Create a cleaner thread that runs on a set schedule that loops through the map entries, examining the time fields and removing expired ones as needed.

But there are lots of considerations about memory usage, timing, thread conditions, etc. If at all possible, I would turn to ehcache rather than attempting to implement a cache directly. If one needs to only use the JDK, I would look carefully at the concepts in ehcache to understand some of the design considerations.

Prior to ehcache I implemented something similar to what I outlined above. For a small number of objects, it can be made to work. For complex situations, it is simply easier and more efficient to use existing libraries.

Edit: add quick conceptual example. Here we see a CacheEntity object that holds a given data time, and will also track the creation time and allow for updating the last accessed time.

public class CacheEntity<T>
{
private T data;
private final long createTime = System.currentTimeMillis();
private long lastAccessTime = createTime;

public CacheEntity(T data)
{
    this.data = data;
}


public void touchLastAccessTime()
{
    lastAccessTime = System.currentTimeMillis();
}

public long getLastAccessTime()
{
    return lastAccessTime;
}

public long getCreateTime()
{
    return createTime;
}

public T getData()
{
    return data;
}
} //class

This class (that has issues; truly quick conceptual example) allows adding to a cache and retrieving data.

public class Cache
{
private final ConcurrentHashMap<String, CacheEntity<MyObject>> theCache =
        new ConcurrentHashMap<>();

public Cache()
{
}

public void addToCache(MyObject obj)
{
    CacheEntity<MyObject> ce = new CacheEntity<MyObject>(obj);
    theCache.putIfAbsent(obj.getName(), ce);
}


public MyObject getData(String key)
{
    MyObject foundData = null;

    CacheEntity<MyObject> ce = theCache.get(key);
    if (ce != null) {
      ce.touchLastAccessTime();
      foundData = ce.getData();
    }

    return foundData;
}

public static class MyObject
{
    String name;
    String title;

    public String getName()
    {
        return name;
    }
}
} //class

One would then need to create a background thread (I would use the Executor system, but whatever) that would, on a periodic basic, run through the cache looking at the times and removing from the Map as appropriate.

Comments