最少阻塞 java 缓存

least blocking java cache

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

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() 执行不应相互阻塞...

我们如何做到这一点?


更新

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

例如,有一种货币类型仅通过其构建器实例化并且是不可变的,构建器本身在验证传入的参数有效后会在静态上下文中检查这个所谓的全局缓存以查看是否已经存在实例创建...好吧你让我...

这不是一个枚举用例,因为系统将动态添加新的货币、市场甚至交换实例,所有这些实例都应该松散耦合并且只实例化一次...(也是为了防止繁重的 GC)

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

我发现这个 link 很有用 http://tutorials.jenkov.com/java-concurrency/read-write-locks.html

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

实际上,我今天在布尔塞尔的 FOSDEM 会议上就此发表了演讲。在此处查看幻灯片:http://www.slideshare.net/cruftex/cache2k-java-caching-turbo-charged-fosdem-2015

基本上可以使用GoogleGuava,但是Guava是一个使用LRU的缓存,所以还是需要一个synchronized block。我在 cache2k 中探索的东西使用了一种高级逐出算法,它不需要对缓存访问进行列表操作,所以根本没有锁。

cache2k 在 Maven Central 上,添加 cache2k-api 和 cache2k-core 作为依赖并初始化缓存:

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

如果你只有缓存命中,cache2k 比 Guava 快 5 倍,比 EHCache 快 10 倍。对于您的使用模式,例如使用 Currency 类型,您可以 运行 通过配置读取缓存并添加负责构建 Currency 实例的缓存源。

因此,您不一定要留意缓存。对于货币示例,您不需要缓存,因为货币实例的数量有限 space。如果你想对可能的非限制 space 做同样的事情,缓存是更通用的解决方案,因为你必须限制资源消耗。我探索的一个例子是将其用于格式化日期。参见:https://github.com/headissue/cache2k-benchmark/blob/master/zoo/src/test/java/org/cache2k/benchmark/DateFormattingBenchmark.java

有关 cache2k 的一般问题,请随时 post 在堆栈溢出时提出。