Вопрос по java, oop, design-patterns, hashmap – Об использовании Singleton на основе Enum для кэширования больших объектов (Java)

1

Есть ли лучший способ для кэширования некоторых очень больших объектов, которые могут быть созданы только один раз, и поэтому должны быть кэшированы? В настоящее время у меня есть следующее:

public enum LargeObjectCache {  
    INSTANCE; 

    private Map<String, LargeObject> map = new HashMap<...>();

    public LargeObject get(String s) {  
        if (!map.containsKey(s)) {
            map.put(s, new LargeObject(s));
        }
        return map.get(s);
    }
}  

Существует несколько классов, которые могут использовать LargeObjects, поэтому я решил использовать синглтон для кэша вместо передачи LargeObjects каждому классу, который его использует.

Кроме того, карта не содержит много ключей (один или два, но ключ может различаться при разных запусках программы), поэтому есть ли другая, более эффективная карта для использования в этом случае?

Ваш Ответ

2   ответа
4

чтобы у вас не было двух экземпляров с одинаковыми именами. Это имеет большое значение для небольших карт, но вы можете избежать одного вызова, который может сделать это быстрее.

public LargeObject get(String s) {  
    synchronized(map) {
        LargeObject ret = map.get(s);
        if (ret == null) 
            map.put(s, ret = new LargeObject(s));
        return ret;
    }
}
Если вы используете synchronizedMap (), вы можете иметь состояние гонки, при котором два или более потоков блокируют map.get (), а несколько потоков выполняют map.put (). Каждая операция является поточно-ориентированной, но не как пара операций.
Но если по требованию необходимо создавать атомарные экземпляры, одновременные составные операции putIfAbsend () очень дороги для больших объектов.
Если вы используетеsynchronizedMap (или дажеCuncurrentMap) тогда вы можете создать объект дважды. Вместо того, чтобы блокировать всю карту на время вашегоLargeObject создание, вы можете заблокировать для определенного ключа (скажем, с помощьюFuture). Однако код становится все сложнее.
Вы правы, спасибо. Однако в javadoc он упоминает об обеспечении безопасности потоков: «Обычно это достигается путем синхронизации с некоторым объектом, который естественным образом инкапсулирует карту. Если такого объекта не существует, карта должна быть «обернута». используя метод Collections.synchronizedMap. & quot ;. Итак, каковы различия между использованием synchronized (map) и использованием Collections.synchronizedMap (map) или даже использованием ConcurrentHashMap & lt; String, LargeObject & gt; ? Все они, кажется, имеют одну и ту же цель, но я полагаю, что должны быть различия. João Silva
2

вам необходимо обратиться к безопасности потоков. Простое использование Collections.synchronizedMap () не делает его полностью правильным, поскольку код влечет за собой сложные операции. Синхронизация всего блока является одним из решений. Однако использование ConcurrentHashMap приведет к гораздо более параллельному и масштабируемому поведению, если оно критично.

public enum LargeObjectCache {  
    INSTANCE; 

    private final ConcurrentMap<String, LargeObject> map = new ConcurrentHashMap<...>();

    public LargeObject get(String s) {
        LargeObject value = map.get(s);
        if (value == null) {
            value = new LargeObject(s);
            LargeObject old = value.putIfAbsent(s, value);
            if (old != null) {
                value = old;
            }
        }
        return value;
    }
}

Вам необходимо использовать его именно в этой форме, чтобы иметь правильное и наиболее эффективное поведение.

Если вы должны убедиться, что только один поток может даже создать экземпляр значения для данного ключа, тогда возникает необходимость обратиться к чему-то вроде карты вычислений в Google Collections или примера памятника в книге Брайана Гетца «Параллельность Java на практике». ,

Похожие вопросы