阻塞最少的java缓存

时间:2015-01-30 19:08:29

标签: java caching concurrency thread-safety

假设我们想为特定实体实现缓存。

class Cache {
    private static Map<String, Object> cache = new HashMap<>();

    public static Object get(String id) {
        assert notNullOrEmpty(id);
        return cache.get(id);
    }

    public static Object add(String id, Object element) {
        assert notNullOrEmpty(id) && notNull(element);

        if(cache.containsKey(id)) return cache.get(id);

        cache.put(id, element);
        return element;
    }
}

现在我们要确保这是线程安全的,最重要的是在数据访问和性能方面是最优的(我们不想在没有必要时阻止)。例如,如果我们将两个方法都标记为已同步,那么我们将无用地阻止两个并发的get()调用,这些调用可以完美地在没有块的情

所以我们只想在add()处于进程中时阻塞get(),并且只在至少有一个get()或add()正在进行时才阻止添加。多个并发的get()执行不应该相互阻塞......

我们如何做到这一点?


更新

实际上这不是缓存,只是我用来描述问题的用例,实际目的是创建一个单一实例存储...

例如,有一种货币类型只能通过其构建器进行实例化,并且在验证传入的参数是否有效之后是构建器本身,在静态上下文中检查此所谓的全局缓存以查看是否已创建实例。 ..你有我......

这不是枚举用例,因为系统会动态添加新的Currency,Market甚至Exchange实例,这些实例应该松散耦合并仅实例化一次......(同时防止重GC)

所以要澄清这个问题......想想全球并发问题而不是特定的问题。

我发现此链接非常有用http://tutorials.jenkov.com/java-concurrency/read-write-locks.html

我想JDK中已经有一些锁类型用于此目的,但还不确定。

1 个答案:

答案 0 :(得分:1)

实际上我今天在Burssels举行的FOSDEM会议上就此发表了演讲。请参阅此处的幻灯片:http://www.slideshare.net/cruftex/cache2k-java-caching-turbo-charged-fosdem-2015

基本上你可以使用Google Guava,但是,由于Guava是一个使用LRU的缓存,所以仍然需要一个同步块。我在cache2k中探索的东西是使用高级驱逐算法,不需要对缓存访问进行列表操作,所以完全锁定。

cache2k在maven central上,将cache2k-api和cache2k-core添加为依赖项并使用以下命令初始化缓存:

cache = 
  CacheBuilder.newCache(String.class, Object.class)
    .implementation(ClockProPlusCache.class)
    .build();

如果你只有缓存命中率,那么cache2k比番石榴快5倍,比EHCache快10倍。对于您的使用模式,使用Currency类型,您可以在读取配置中运行缓存,并添加一个缓存源,负责构建Currency实例。

所以,你不一定要注意缓存。对于货币示例,您不需要缓存,因为货币实例的空间有限。如果您希望对可能的非限制空间执行相同操作,则缓存是更通用的解决方案,因为您必须限制资源消耗。我探索过的一个例子是将它用于格式化日期。请参阅:https://github.com/headissue/cache2k-benchmark/blob/master/zoo/src/test/java/org/cache2k/benchmark/DateFormattingBenchmark.java

关于cache2k的一般问题,请随意将它们发布到堆栈溢出。