简体   繁体   中英

How to update a LinkedList value stored in cache using Guava's LoadingCache

I am trying to utilize LoadingCache from the Guava library to cache a LinkedList .

LoadingCache<Integer, LinkedList<String>> cache;

I've setup a CacheLoader to handle misses, which is working fine. However there is another system that needs to submit updates to existing cache entries. Each update needs to be appended to the LinkedList and will arrive at a fairly quick rate (thousands per minute). Finally, it needs to be thread safe.

Here is a naive approach that illustrates the logic but is not thread safe:

public void add(Integer key, String value) {
    LinkedList<String> list = cache.get(key);
    list.add(value);
    cache.put(key, list);
}

Any advice on how to make this work? I can look at other libraries but Guava 14 is already a dependency of this codebase and would be very convenient.

The last line in

public void add(Integer key, String value) {
    LinkedList<String> list = cache.get(key);
    list.add(value);
    cache.put(key, list);
}

is not needed as you already modify the object obtained from the cache. Maybe all you need is

public void add(Integer key, String value) {
    LinkedList<String> list = cache.get(key);
    synchronized (list) {
        list.add(value);
    }
}

It depends on what eviction happens. If there's no eviction at all, then it will work. If an entry can get evicted before the updating method finishes, then you're out of luck.

Nonetheless, there's a simple solution: Using a global lock would work, but obviously inefficiently. So use a list of locks:

private static final CONCURRENCY_LEVEL = 64; // must be power of two
List<Object> locks = Lists.newArrayList(); // an array would do as well
for (int i=0; i<CONCURRENCY_LEVEL; ++i) locks.add(new Object());

public void add(Integer key, String value) {
    synchronized (locks.get(hash(key))) {
        cache.get(key).add(value);
    }
}

where hash - depending on the distribution of your keys - can be as simple as key.intValue() & (CONCURRENCY_LEVEL-1) or something like here what sort of randomizes the distribution.


While my above list of locks should work, there's Striped.lock(int) in Guava, which makes it a bit simpler and takes care of padding (see false sharing for what it's good for) and whatever.


Most probably you should not use LinkedList as it's nearly always slower than ArrayList .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM