Java - 在许多线程的缓存中保存变量

时间:2015-06-30 08:00:27

标签: java multithreading thread-safety java-8

我希望有一个类在多个线程的缓存中保存变量。

在ConcurrentHashMap中保存它是一个好习惯吗?

public class CacheMap {

    private static Map<Object, Object> cacheMap = new ConcurrentHashMap<>();

    public static void set(Object key, Object value) {
        cacheMap.put(key, value);
    }

    public static Object get(String key) {
        return cacheMap.get(key);
    }

}

3 个答案:

答案 0 :(得分:4)

在当前实现中,您可能会计算两次相同的值。我认为使用模式目前看起来像这样:

Object value = cache.get(key);
if(value == null) {
    value = computeValue();
    cache.set(key, value);
}
// use value here

如果有多个线程同时要求相同的值,那么如果有这样的代码,你可能会多次计算相同的值。此外,如果value是长期存在的对象,它们将使用不同的结果对象,这可能导致不必要的内存浪费。

更好的解决方案是使用ConcurrentHashMap的{​​{3}}方法:

Object value = map.computeIfAbsent(key, k -> computeValue());

这样一来,它保证每个键只计算一次。因此,我不建议在缓存实现中使用两个getset方法,而是建议采用单computeIfAbsent方法。

您的代码中也存在一些小问题,例如keyget方法中set的不同类型以及缺少通用参数。

答案 1 :(得分:0)

我建议在volatile中使用Map,但其余模式似乎没问题。

来自this answer

  

易失性变量:如果两个线程(假设t1和t2)正在访问同一个对象并更新一个声明为volatile的变量,则意味着t1和t2可以创建自己的本地缓存除了声明为volatile的变量之外的对象。所以volatile变量只有一个主副本,它将由不同的线程更新,一个线程对volatile变量的更新将立即反映到另一个Thread。

private volatile Map<Object, Object> cacheMap = new ConcurrentHashMap<>();

另外check this article了解更多信息。

答案 2 :(得分:-1)

在并发性方面,您的解决方案可以正常运行。 ConcurrentHashMap是线程安全的,因此它可以被许多线程使用。

我建议您小心内存泄漏,因为如果没有定义清理程序,缓存可以无限制地增长。

您也可以定义自己的缓存。例如,我喜欢为我的缓存对象使用LRU(最近最少使用)队列。使用LRU队列,您将确保缓存不会无限制地增长,并且将从缓存中删除的元素将是更久以前使用的元素。

您可以实现LRU队列,例如可能的实现可能是:

public class MyLRUHashMap<K, V> extends LinkedHashMap<K, V> {

    private static final long serialVersionUID = -3216599441788189122L;

    private int maxCapacity = 1000;

    protected MyLRUHashMap(int capacity) {
        super(capacity);
        maxCapacity = capacity;
    }

    protected MyLRUHashMap(int capacity, boolean accessOrder) {
        super(capacity, 0.75f, accessOrder);
        maxCapacity = capacity;
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        return (this.size() >= maxCapacity);
    }
}

为了创建“MyHashMap”的线程安全变量,你必须以这种方式实例化它:

Map<Object, Object> MyCacheMap = Collections.synchronizedMap(new MyLRUHashMap(capacity, accessOrder));

希望它有所帮助! :)