Thursday, July 19, 2012

Concurrent Maps for lightweight caching

Here is the Dave best practice when using a map for caching purposes in a java server environment:

Don't naively use Java maps as a global cache in a Java Server environment. 

Instead:

If it's a create-once, read-many type cache.
Use a Guava ImmutableMap and ImmutableMapBuilder:

public class DaveMapDemo1 {

   
//note: Person is immutable
   
private final ImmutableMap<String,Person> map;

   
public DaveMapDemo1() {

       ImmutableMap.Builder<String,Person> builder =
               
new ImmutableMap.Builder<String,Person>();
       builder.put(
"123",new Person("Dave","Ford"));
       builder.put(
"222",new Person("Joe","Blow"));
       map = builder.build();

   }

   
public Person getPerson(String id){
       
return map.get(id);
   }

}


If it's a get-or-create type cache.
If the cache is populated lazily, use Guava's LoadingCache and CacheBuilder classes (these replace the Guava ConcurrentMap implementations):

public class DaveMapDemo2 {

   
//note: Person is immutable
   
final LoadingCache<String, Person> map;

   
public DaveMapDemo2() {

       map = CacheBuilder.newBuilder()
               .maximumSize(
1000)
               .expireAfterWrite(
10, TimeUnit.MINUTES)
               .build(
                       
new CacheLoader<String, Person>() {
                           
public Person load(String id) {
                               
return lookupPersonFromDatabase(id);
                           }
                       });

   }

   
public Person getPerson(String id) throws ExecutionException {
       
return map.get(id);
   }

   
private Person lookupPersonFromDatabase(String id) {
       
// yada yada yada
   }
}


Summary
Notice that both examples:

  1. Have no mutable state
  2. Do not use the keyword synchronized

No comments: