Thread-safe cache of one object in java

Igor Mukhin picture Igor Mukhin · Sep 3, 2010 · Viewed 19.5k times · Source

let's say we have a CountryList object in our application that should return the list of countries. The loading of countries is a heavy operation, so the list should be cached.

Additional requirements:

  • CountryList should be thread-safe
  • CountryList should load lazy (only on demand)
  • CountryList should support the invalidation of the cache
  • CountryList should be optimized considering that the cache will be invalidated very rarely

I came up with the following solution:

public class CountryList {
    private static final Object ONE = new Integer(1);

    // MapMaker is from Google Collections Library    
    private Map<Object, List<String>> cache = new MapMaker()
        .initialCapacity(1)
        .makeComputingMap(
            new Function<Object, List<String>>() {
                @Override
                public List<String> apply(Object from) {
                    return loadCountryList();
                }
            });

    private List<String> loadCountryList() {
        // HEAVY OPERATION TO LOAD DATA
    }

    public List<String> list() {
        return cache.get(ONE);
    }

    public void invalidateCache() {
        cache.remove(ONE);
    }
}

What do you think about it? Do you see something bad about it? Is there other way to do it? How can i make it better? Should i look for totally another solution in this cases?

Thanks.

Answer

Gareth Davis picture Gareth Davis · Sep 3, 2010

google collections actually supplies just the thing for just this sort of thing: Supplier

Your code would be something like:

private Supplier<List<String>> supplier = new Supplier<List<String>>(){
    public List<String> get(){
        return loadCountryList();
    }
};


// volatile reference so that changes are published correctly see invalidate()
private volatile Supplier<List<String>> memorized = Suppliers.memoize(supplier);


public List<String> list(){
    return memorized.get();
}

public void invalidate(){
    memorized = Suppliers.memoize(supplier);
}