java - 最少阻塞 java 缓存

标签 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() 正在处理时才阻止 add。多个并发的 get() 执行不应相互阻塞...

我们如何做到这一点?


更新

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

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

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

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

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

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

最佳答案

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

基本上你可以使用Google Guava,但是,由于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 实例的缓存源。

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

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

关于java - 最少阻塞 java 缓存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28242977/

相关文章:

java - 如何实现Guava缓存来存储和获取不同类型的对象?

java - 无法使用 SQL 工具查询现有的 Ignite 缓存

java - 扩展大型数据集的大型并发 API 请求

multithreading - 纯函数式编程中的竞争条件

java - 从指定的 URL 读取 CSV 文件

java - 如何包含jpcap库?

c - 如何预加载大型数组以并行缓存?

C 中的并发程序(无法从方法中获得正确的输出答案)

java - Spring REST Web 服务 appContext 失败

java - java中有哪些管理 session 数量和消除不需要的 session 的策略?