ThreadLocal HashMap vs ConcurrentHashMap用于线程安全的未绑定缓存

Mai*_*ian 8 java performance caching concurrenthashmap thread-local

我正在创建一个具有以下特征的memoization缓存:

  • 高速缓存未命中将导致计算和存储条目
    • 这个计算非常昂贵
    • 这种计算是幂等的
  • 无界限(条目从未删除),因为:
    • 输入将导致最多500个条目
    • 每个存储的条目都很小
    • 缓存相对短缺(通常不到一小时)
    • 总的来说,内存使用不是问题
  • 将有数千个读取 - 在缓存的生命周期中,我预计99.9%+缓存命中
  • 必须是线程安全的

什么会有一个优越的性能,或在什么条件下一个解决方案优于另一个解决方案?

ThreadLocal HashMap:

class MyCache {
    private static class LocalMyCache {
        final Map<K,V> map = new HashMap<K,V>();

        V get(K key) {
            V val = map.get(key);
            if (val == null) {
                val = computeVal(key);
                map.put(key, val);
            }
            return val;
        }
    }

    private final ThreadLocal<LocalMyCache> localCaches = new ThreadLocal<LocalMyCache>() {
        protected LocalMyCache initialValue() {
            return new LocalMyCache();
        }
    };

    public V get(K key) {
        return localCaches.get().get(key);
    }
}
Run Code Online (Sandbox Code Playgroud)

ConcurrentHashMap的:

class MyCache {
    private final ConcurrentHashMap<K,V> map = new ConcurrentHashMap<K,V>();

    public V get(K key) {
        V val = map.get(key);
        if (val == null) {
            val = computeVal(key);
            map.put(key, val);
        }
        return val;
    }
}
Run Code Online (Sandbox Code Playgroud)

我认为如果有很多线程,ThreadLocal解决方案最初会因为每个线程的所有缓存未命中而缓慢,但是超过数千次读取,摊销成本将低于ConcurrentHashMap解决方案.我的直觉是否正确?

还是有更好的解决方案?

far*_*992 4

使用 ThreadLocal 作为缓存不是一个好习惯

在大多数容器中,线程通过线程池重用,因此永远不会被GC。这会导致一些有线的事情

使用 ConcurrentHashMap 你必须管理它以防止内存泄漏

如果你坚持,我建议使用 week 或 soft ref 并在丰富的 maxsize 之后驱逐

如果您正在寻找内存缓存解决方案(不要重新发明轮子),请尝试番石榴缓存 http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/cache/CacheBuilder.html