寻找简单的Java内存缓存

123
我正在寻找一个简单的Java内存缓存,具有良好的并发性能(LinkedHashMap不够好),并且可以定期序列化到磁盘中。
我需要的一个特性是“窥视”对象的方式,但这被证明很难实现。所谓“窥视”,指的是在不比其本身更长的时间内从缓存中检索对象。
更新:我忘了提到一个额外的要求,那就是我需要能够原地修改缓存对象(它们包含浮点数数组)。
有人能提供任何建议吗?

1
我正在寻找类似的东西,它是“进程内”并且更轻量级。我想使用它在Eclipse插件中将一些数据存储在堆中。对于我而言,Ehcache和JCS似乎太重/分布式/J2EE了。 - Uri
1
可能是轻量级Java对象缓存API的重复问题。 - Raedwald
我会推荐使用Apache Ignite(https://ignite.apache.org/)。 - B. S. Rawat
2
这个问题已经关闭了6年,但今天人们仍然在思考,这表明SO的主持人系统正在失败。 - dustinevan
9个回答

71

自从最初发布这个问题后,Google的Guava库现在包含了一个功能强大且灵活的缓存。我建议使用它。


5
请注意,Spring不再支持Guava缓存:https://dev59.com/J1cP5IYBdhLWcg3wlq73 - sinner
@sanity 实现了 Jcache 吗? - Gaurav
2
Caffeine 是一个好用且性能出色的缓存库。它是一个基于 Java 8 的高性能、接近最优的缓存库。Caffeine 使用类似 Google Guava 的 API 提供了内存缓存的功能。 - Slavus

38

Ehcache 是一个相当不错的解决方案,可通过一种方式进行窥视(使用 getQuiet() 方法),这样就不会更新空闲时间戳。在内部实现中,Ehcache 采用了一组类似于 ConcurrentHashMap 的映射表,因此具有类似的并发优势。


2
谢谢,另外一个问题:如果我从EHcache中检索一个对象(比如一个数组),并对其进行修改,那么这个对象会在缓存中更新吗?也就是说,EHCache是否维护对这些对象的引用? - sanity
1
我相信是这样,但为了安全起见,您必须适当地锁定对象。 - Alex Miller
我喜欢ehcache,但它是一个10mb的jar包。太大了。 - markthegrea

34

如果您需要简单的东西,这个是否符合要求?

Map<K, V> myCache = Collections.synchronizedMap(new WeakHashMap<K, V>());

它不会保存到磁盘,但你说你想要简单的方式...

链接:

(正如Adam所评论的,同步地图会有性能损失。并不是说这个想法没有问题,但作为一种快速而脏的解决方案足够了。)


4
同步整个庞大的地图是一个很大的负担。你可以轻松地将弱类型存储在并发哈希映射中,并定期删除它们。 - Adam Gent
11
ConcurrentHashMap 在性能方面优于 Collections.synchronizedMap。 - Pablo Moretti
11
就性能而言,ConcurrentHashMap 绝对表现更佳,但它与 WeakHashMap 不同,不能让垃圾回收器回收内存。这可能对你很重要,也可能不重要。 - Evan
7
请使用ConcurrentHashMap<WeakReference<T>>。参考文档:http://docs.oracle.com/javase/7/docs/api/java/lang/ref/WeakReference.html - jdmichal

21
另一个用于Java内存缓存的选择是cache2k。其内存性能优于EHCache和Google Guava,请参阅cache2k基准测试页面
使用模式类似于其他缓存。以下是一个示例:
Cache<String,String> cache = new Cache2kBuilder<String, String>() {}
  .expireAfterWrite(5, TimeUnit.MINUTES)    // expire/refresh after 5 minutes
  .resilienceDuration(30, TimeUnit.SECONDS) // cope with at most 30 seconds
                                          // outage before propagating 
                                          // exceptions
  .refreshAhead(true)                       // keep fresh when expiring
  .loader(new CacheLoader<String, String>() {
    @Override
    public String load(final String key) throws Exception {
      return ....;
    }
  })
  .build();
String val = cache.peek("something");
cache.put("something", "hello");
val = cache.get("something");
如果你已经将Google Guava作为依赖项,那么尝试使用Guava缓存可能是一个不错的选择。

如果你已经将Google Guava作为依赖项,那么尝试使用Guava缓存可能是一个不错的选择。


如果我关闭我的应用程序,缓存会销毁所有已注册的数据吗? - Menai Ala Eddine - Aladdin

10
您可以轻松使用imcache。以下是示例代码。
void example(){
    Cache<Integer,Integer> cache = CacheBuilder.heapCache().
    cacheLoader(new CacheLoader<Integer, Integer>() {
        public Integer load(Integer key) {
            return null;
        }
    }).capacity(10000).build(); 
}

10

10

试试这个:

import java.util.*;

public class SimpleCacheManager {

    private static SimpleCacheManager instance;
    private static Object monitor = new Object();
    private Map<String, Object> cache = Collections.synchronizedMap(new HashMap<String, Object>());

    private SimpleCacheManager() {
    }

    public void put(String cacheKey, Object value) {
        cache.put(cacheKey, value);
    }

    public Object get(String cacheKey) {
        return cache.get(cacheKey);
    }

    public void clear(String cacheKey) {
        cache.put(cacheKey, null);
    }

    public void clear() {
        cache.clear();
    }

    public static SimpleCacheManager getInstance() {
        if (instance == null) {
            synchronized (monitor) {
                if (instance == null) {
                    instance = new SimpleCacheManager();
                }
            }
        }
        return instance;
    }

}

在其他答案中已经讨论了依赖于同步映射的问题。 - sanity
@sergeyB:我正在寻找一个类似的解决方案,它可以简单地实现缓存...谢谢。 - Prakruti Pathik
1
public static SimpleCacheManager getInstance() { 应该修改为 public synchronized static SimpleCacheManager getInstance() {,否则 if (instance == null) { 不是线程安全的。在这种情况下,您可以完全删除监视器对象,因为所有的getInstance()调用都会发生同步,参见: http://www.javaworld.com/article/2073352/core-java/simply-singleton.html?page=2 - Can Kavaklıoğlu
我可以建议使用枚举来实现单例模式吗?https://dzone.com/articles/java-singletons-using-enum - Erk

3

看起来 @sanity 想要一个轻量级缓存。 - Rodolfo

1

试试Ehcache?它允许您插入自己的缓存过期算法,以便控制峰值功能。

您可以将其序列化到磁盘、数据库、跨集群等等...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接