Implementing a simple cache with Java

In this post I’ll present a simple cache written in Java. It can be used to store objects of any kind or specific types only. The objects will expire after a certain time that can be customized too. Since this cache is really simple I didn’t implement a caching strategy nor an option for a maximum capacity.

You can download the Eclipse project as tar or zip or browse the code online here.

Basic operations

The cache supports these basic operations: you can put and get objects. If you put an object you can specify a time in seconds – after this time period the object will be remove from the cache.

Getting an object from the cache is simple too and you’ve also got the possibility to specify the type of the object you’re retrieving from the cache – this adds some type safety.

Have a look at this sample code:

SimpleCache<Integer> intCache = new SimpleCache<Integer>();
intCache.put("one", 1);
assertEquals(new Integer(1), intCache.get("one"));

This creates a SimpleCache that holds objects derived from Integer. Since you parameterized this cache with Integer you’ll only be able to put and get integers.

If you want to save all different kinds of objects in the cache that’s no problem at all. If you do this you can use the convenience method get which performs a cast for you. Have a look at this example:

SimpleCache cache = new SimpleCache();
cache.put("one", 1);
cache.put("str", "Some string");
assertEquals(new Integer(1), cache.get("one", Integer.class));
assertEquals("Some string", cache.get("str", String.class));

However, I think it’s better to create several instances of SimpleCache that stores objects of a specific type.

Removing expired objects

Once an object is expired we want to remove it from the cache. The SimpleCache class supports two ways of removing old objects:

  1. if the get method is called we’ll check whether the object that’s about to be returned is expired. If that’s the case we’ll remove it from the cache.
  2. a thread that runs periodically removes expired objects from the cache.

Both features reside in implementations of Runnable so they can be executed in separate threads. This way the basic operations will not be slowed down by this kind of housekeeping.

With Java 5 it’s easy to schedule recurring tasks with the Executors class. To remove expired objects from the cache we can use this code:

Runnable cleaner = new Runnable() {
  public void run() { /* remove expired objects here */ }
};
Executors.newScheduledThreadPool(1)
         .scheduleWithFixedDelay(cleaner, 0, 30, TimeUnit.SECONDS);

Conclusion

I presented a very simple cache written in Java that stores objects of any kind. It’s configurable, type safe and easy to use; it should be pretty easy to integrate this cache into an existing project.

I’ve tried to keep the cache fast: certain tasks will be executed in threads to speed things up. But don’t worry because the threads are easy to maintain and to understand.

36 comments ↓

  • Kevin says:
    Excellent post. This is exactly what I needed. It is really helping me re-learn a bunch of Java stuff that I should already know :-)
  • Vladislav says:
    Nice job, Christian!

    I just like to pay attention that if you put or remove an element to the cache during the expiration removal process (name : expire.keySet()) you will get ConcurrentModificationAccessException for sure.

    A simple solution could be to surround your code with dedicated synchronization lock.

    Cheerio,
    Vladislav

  • Hi Vladislav,
    although I haven’t experienced this exception yet, you’re right: we should add some type of locking here. Thank you for the hint!
  • sunil kumar says:
    Good stuff.
    good to start with basic caching mechanism
  • Bass says:
    Great intro to caching. But can you explain why you need a separate thread each time the get method is called? It appears to be an on-demand rather than a housekeeping activity so why would you need a another thread just for that?
  • Hi Bass,
    we don’t really need this extra thread because the get method makes sure that it doesn’t return old data. But in case the get method isn’t called and we keep adding objects to the cache over and over again, none of them will be removed which may result in an OutOfMemoryError – to prevent this I added this housekeeping thread.
  • Adriaan says:
    Hi Christian,

    The approach I used to implement a cache like yours was to use java.util.LinkedHashmap. This offers functionality to set a limit to the size of the cache and remove the least recently used item from the cache when the limit is exceeded. Advantages are that you don’t need a scheduled worker thread to clean up your cache and the maximum size of the cache can be pre-determined.

  • Hi Adriaan,
    thanks for the hint – it’s a great idea!
  • John says:
    hi Christian,

    How do you modify the code as Vladislav mentioned to prevent ConcurrentModificationAccessException from happening during the expiration removal process?

    Will this work (adding synchronized keyword to the run method)?

    public synchronized void run() {
    for (final String name : expire.keySet()) {


    }
    }

  • Hi John,
    I’m pretty sure that adding synchronized to the run method of the thread won’t cut it. Instead, you should sync on this to make sure that both objects named objects and expire can’t be modified during the expiration removal process.
  • John says:
    Hi Christian,

    Here’s what I understood from you. Below is what I think should work now. Correct me if I’m wrong.

    private Runnable createRemoveRunnable(final String name)
        {
            return new Runnable()
            {
                public void run()
                {
                    synchronized (this)
                    {
                        objects.remove(name);
                        expire.remove(name);
                    }
                }
            };
        }

     

  • Hi John,
    that’s exactly what I thought about.
  • vikas says:
    Hi Christian,

    Great and simple solution.

  • swaroop says:
    The piece of stuff u provided is good but u should have provided the full code so that we can understand whole program
  • Hi swaroop,
    you can download the Eclipse project as tar or zip or browse the code online here.
  • Michael says:
    Thanks for your solution, helped a lot!
  • Rohit Sachan says:
    Hi Christian,
    Good stuff, I was doing almost the same thing by wrapping the Object in an inner class with an extra data member “time stamp” attached and scavanging in backgroung via a thread.
    But my design as well yours has a performance issue if the cache is storing Millions of records (that happens in case of Financial Soultions when market moves every time in sp. Equities and FX ) in a minute ( i.e. expiry time is 2 minutes)
    Because we have to iterate through keyset of millions of records.
    I am looking for a much effecient solution, any comments are welcomed.
  • Hi Rohit,
    as the name implies SimpleCache is a really simple solution to cache objects of a certain type. It isn’t very sophisticated but may serve as a foundation for a more intelligent implementation.
    In your case with lots and lots of records I could think of the following solutions:

    1. limit the amount of cached objects and throw away the oldest objects on expiry not making any use of expiry after a certain amount of time.
    2. split the cache into various smaller segments that can be handled in a reasonable amount of time. Right now the implementation has got a map of objects and you could extend that to a map containing maps of objects. During the expiry run only work on a subset of these maps in the larger map.

    Both solutions may help you limiting the set you will have to work on during an expiry run.

  • Maldalapu says:
    Is it required to have a separate thread to remove entries from cache ? Like below

    public void run() {
      cacheStore.remove(name);
      expire.remove(name);
    }

    Can’t we keep the same inside after threads.execute(createRemoveRunnable(name)); in removeExpired() method ?

  • Hi Maldalapu,
    sure you can do this. Remember encapsulating these two operations in a synchronized block, like it was mentioned earlier in the comments.
  • Maldalapu says:
    Here is what I have done:

    private void createRemoveRunnable(final K name) {
      synchronized (this) {
        cacheStore.remove(name);
        expire.remove(name);
      }
    }

    And is it required to keep synchronized block in removeExpired() method ? If yes please let me know how do I do that ?

  • Hi Maldalapu,
    sure, that’s one way to do it. You only need one synchronized block and won’t have to add synchronized to every method.
  • Thanks a Lot Christian,
    I got what I needed.
    It is described in very simple and precise manner. I like it. It would help me out in my exisiting project.
  • Indrajit Yadav says:
    Hi All,

    I am new for caching concept but after reading simple caching in java, and all your’s comments i am happy it is giving more information to understand this concept and proceed further.

    Thanks !

  • Dear Indrajit,
    I’m glad this post helped you. Don’t forget to checkout the source code and play with it a bit – this way you’ll probably understand the concepts even better.
  • Kamel says:
    Hi Christian,
    Thanks so much about your code, I have noticed in comments we sill need to but synchronize block around maps when tries to remove. Can you please explain to me why I need that point as I can see you already creating synchronized map. or what if you used hashtable instead. Thanks
  • Hi Kamel,
    if you’re using a synchronized map you don’t have to encapsulate it inside a synchronized block; the same holds true for a synchronized HashTable, which is just a certain implementation of a map.
    If you checkout the code in the comment above in more detail you’ll notice that two objects are altered inside the synchronized block. That said, the synchronized block makes sure that changing both objects is a single atomic operation.
  • F1 says:
    i m using purple.jar and i have written a logic to create cacheKey and store the list of data, when the user sign off’s i want to remove the cache and make it blank.
    how i ca achieve this..?
  • Hi F1,
    please ask the authors of purple.jar how to do this since I’ve never heard of this software before and don’t know how it works.
  • priya says:
    please tell me we need to add any extra headers for the cache
  • Anupam says:
    As some one pointed out earlier, it is easier to use LinkedHashMap if you want to implement a simple LRU cache. For heavily multi threaded applications, I would recommend ConcurrentHashMap instead of Synchronized Hashmap. Here is very good article on concurrent hash maps http://www.ibm.com/developerworks/java/library/j-jtp08223/index.html
  • Hi Anupam,
    thank you for the hint and the link to the great article – much appreciated.