2010-01-15 26 views
15

का उपयोग करके कैश को कार्यान्वित करना मैं वेब जावा एप्लिकेशन में हेवीवेट ऑब्जेक्ट्स की एक साधारण कैशिंग को कार्यान्वित करना चाहता हूं। लेकिन मैं यह नहीं समझ सकता कि इसे ठीक से कैसे किया जाए।जावा कंसूरेंट हैश मैप

क्या मुझे कुछ याद आ रहा है या ConcurrentHashMap विधियां (putIfAbsent, ...) पर्याप्त नहीं हैं और अतिरिक्त सिंक्रनाइज़ेशन की आवश्यकता है?

क्या ऐसा करने के लिए एक बेहतर सरल एपीआई (मेमोरी स्टोरेज में, कोई बाहरी कॉन्फ़िगरेशन नहीं है)?

पी

+1

बस सोच रहा है: कैशिंग के लिए वास्तव में आपकी क्या आवश्यकताएं हैं? क्या आपको अपने भारी वजन वस्तु के पूर्ण संक्रमण को बंद करने की आवश्यकता है ताकि यह आपके एप्लिकेशन सर्वर के समूह में सुसंगत हो? यदि ऐसा है, तो यह हल करने के लिए एक गैर-मामूली समस्या है, और आप कैश लाइब्रेरी का उपयोग करके बेहतर हो सकते हैं जैसे ehcache। – Alan

उत्तर

14

यदि यह अस्थायी रूप से बात आप कैश करने के लिए कोशिश कर रहे हैं के लिए एक से अधिक उदाहरण के लिए सुरक्षित है, तो आप इस तरह एक "ताला मुक्त" कैश कर सकते हैं:

public Heavy instance(Object key) { 
    Heavy info = infoMap.get(key); 
    if (info == null) { 
    // It's OK to construct a Heavy that ends up not being used 
    info = new Heavy(key); 
    Heavy putByOtherThreadJustNow = infoMap.putIfAbsent(key, info); 
    if (putByOtherThreadJustNow != null) { 
     // Some other thread "won" 
     info = putByOtherThreadJustNow; 
    } 
    else { 
     // This thread was the winner 
    } 
    } 
    return info; 
} 

एकाधिक सूत्र कुंजी के लिए कोई आइटम बनाने और जोड़ने के लिए "दौड़" कर सकते हैं, लेकिन केवल एक को "जीतना" चाहिए।

+0

क्या होगा यदि आप एक अद्यतन विधि चाहते हैं जो किसी दिए गए कुंजी के लिए भारी वस्तु को प्रतिस्थापित/रीफ्रेश करता है? – Paolo1976

+0

या बस MapMaker का उपयोग करें, और केवल एक धागा कभी भारी बना देगा। यदि किसी अन्य थ्रेड को इसकी आवश्यकता होती है, जबकि यह अभी भी इसे बनाने के बीच में है, तो यह परिणाम के लिए बस प्रतीक्षा करेगा। –

+0

@ पाओलो: मैं डाउन-वोटिंग 'मैपमेकर' गुरु को उत्तर दूंगा। – Ken

0

ConcurrentHashMap अपनी आवश्यकताओं के लिए पर्याप्त होना चाहिए putIfAbsent सुरक्षित थ्रेड है।

सुनिश्चित नहीं हैं कि बहुत सरल आप

ConcurrentMap myCache = new ConcurrentHashMap(); 

पॉल

2

प्राप्त कर सकते हैं कैश में "भारी वस्तु" डालने के बजाय, आप प्रकाश कारखाने वस्तुओं का उपयोग एक सक्रिय कैश बनाने के लिए कर सकता है।

public abstract class LazyFactory implements Serializable { 

    private Object _heavyObject; 

    public getObject() { 
    if (_heavyObject != null) return _heavyObject; 
    synchronized { 
     if (_heavyObject == null) _heavyObject = create(); 
    } 
    return _heavyObject; 
    } 

    protected synchronized abstract Object create(); 
} 

// here's some sample code 

// create the factory, ignore negligible overhead for object creation 
LazyFactory factory = new LazyFactory() { 
    protected Object create() { 
    // do heavy init here 
    return new DbConnection(); 
    }; 
}; 
LazyFactory prev = map.pufIfAbsent("db", factory); 
// use previous factory if available 
return prev != null ? prev.getObject() : factory.getObject; 
25

केन के उत्तर के आगे, यदि हेवीवेट ऑब्जेक्ट बनाना जो बाद में फेंक दिया जाता है तो स्वीकार्य नहीं है (आप गारंटी देना चाहते हैं कि प्रत्येक कुंजी के लिए केवल एक वस्तु बनाई जाती है, किसी कारण से), तो आप इसे कर सकते हैं .. .. वास्तव में, मत करो। इसे स्वयं मत करो। का प्रयोग करें google-collections (अब guava) MapMaker class:

Map<KeyType, HeavyData> cache = new MapMaker<KeyType, HeavyData>() 
    .makeComputingMap(new Function<KeyType, HeavyData>() { 
     public HeavyData apply(KeyType key) { 
      return new HeavyData(key); // Guaranteed to be called ONCE for each key 
     } 
    }); 

फिर एक सरल cache.get(key) सिर्फ काम करता है और पूरी तरह से संगामिति और syncrhonization की मुश्किल पहलुओं के बारे में चिंता करने की ज़रूरत नहीं आप निकाल देता है।

ध्यान दें कि आप समाप्ति की तरह, कुछ अधिक सजावटी सुविधाओं जोड़ना चाहते हैं, यह सिर्फ

Map<....> cache = new MapMaker<....>() 
    .expiration(30, TimeUnit.MINUTES) 
    .makeComputingMap(.....) 

है और आप भी आसानी से या तो चाबी या डेटा के लिए नरम या कमजोर मूल्यों का उपयोग कर सकते हैं यदि आवश्यक हो (अधिक के लिए जावाडोक देखना विवरण)

+0

वाह, क्या एक अच्छा और सुरुचिपूर्ण समाधान! – Benjamin

0

मुझे पता है यह एक पुरानी पोस्ट है, लेकिन जावा 8 में यह एक ConcurrentHashMap साथ एक संभावित अप्रयुक्त भारी वस्तु बनाने के बिना किया जा सकता है।

public class ConcurrentCache4<K,V> { 
    public static class HeavyObject 
    { 
    } 

    private ConcurrentHashMap<String, HeavyObject> cache = new ConcurrentHashMap<>(); 

    public HeavyObject get(String key) 
    { 
     HeavyObject heavyObject = cache.get(key); 
     if (heavyObject != null) { 
      return heavyObject; 
     } 

     return cache.computeIfAbsent(key, k -> new HeavyObject()); 
    } 
} 
संबंधित मुद्दे