2012-01-19 14 views
5

मैं, एक कैश, जो अद्वितीय कुंजी है की कुछ हद तक एक विशिष्ट कार्यान्वयन लिखने के लिए की जरूरत है लेकिन डुप्लिकेट मान हो सकते हैं जैसे:थ्रेड उपयोग

"/path/to/one" -> 1 
"/path/to/two" -> 2 
"/path/to/vienas" -> 1 
"/path/to/du" -> 2 

वर्ग गैर अवरुद्ध पढ़ता है की पेशकश करने की जरूरत है/मुख्य लुकअप लेकिन म्यूटेटर्स को सामान्य बनाने/अपडेट/हटा भी है। उदाहरण के लिए, को हटाने के मूल्य 2 परिणाम चाहिए

"/path/to/one" -> 1 
"/path/to/vienas" -> 1 

इस कैश पुस्तकें अब तक इतना लिखने प्रदर्शन कोई मुद्दा नहीं है राईट पल्ला झुकना होगा - समवर्ती लेखन एक दूसरे के ऊपर पर नहीं चलते हैं, तब तक। प्रविष्टियों की कुल संख्या 1000 से कम होने की संभावना है, इसलिए मूल्यों पर कभी-कभी पुनरावृत्ति करना अभी भी सस्ती है।

तो मैं इस (छद्म कोड) की तरह कुछ लिखा है:

// 
// tl;dr all writes are synchronized on a single lock and each 
// resets the reference to the volatile immutable map after finishing 
// 
class CopyOnWriteCache { 
    private volatile Map<K, V> readOnlyMap = ImmutableMap.of(); 

    private final Object writeLock = new Object(); 

    public void add(CacheEntry entry) { 
     synchronized (writeLock) { 
     readOnlyMap = new ImmutableMap.Builder<K, V>() 
      .addAll(readOnlyMap) 
      .add(entry.key, entry.value) 
      .build(); 
     } 
    } 

    public void remove(CacheEntry entry) { 
     synchronized (writeLock) { 
     Map<K, V> filtered = Maps.filterValues(readOnlyMap, somePredicate(entry)); 
     readOnlyMap = ImmutableMap.copyOf(filtered); 
     } 
    } 

    public void update(CacheEntry entry) { 
     synchronized (writeLock) { 
     Map<K, V> filtered = Maps.filterValues(readOnlyMap, somePredicate(entry)); 
     readOnlyMap = new ImmutableMap.Builder<K, V>() 
      .addAll(filtered) 
      .add(entry.key, entry.value) 
      .build(); 
     } 
    } 

    public SomeValue lookup(K key) { 
     return readOnlyMap.get(key); 
    } 
} 

ऊपर लिखने के बाद, मुझे एहसास हुआ ConcurrentHashMap भी प्रदान करता है कि गैर अवरुद्ध पढ़ता है जो मेरे सारे प्रयास व्यर्थ हो जाएगा, लेकिन वहाँ में एक बयान है इसके जावाडोक जो एक भौंह को जन्म देती है:

iterators are designed to be used by only one thread at a time 

तो अगर मैं final ConcurrentHashMap साथ volatile ImmutableMap के उपयोग को बदलने और सभी synchronized ब्लॉकों को हटाने, यह संभव है कि समवर्ती mutators प्रतिस्पर्धा जाएगा एक दूसरे को अमान्य करें? उदाहरण के लिए, मैं कल्पना कर सकता हूं कि remove पर दो समवर्ती कॉल कैसे रेस हालत का कारण बनेंगे, जो पहले remove के परिणामों को पूरी तरह से अमान्य कर देगा।

केवल सुधार मैं देख सकता हूँ कि final ConcurrentHashMapऔर का उपयोग कर synchronized छोड़ने के रूप में वे कर रहे हैं से मैं कम से कम डेटा का अनावश्यक नकल से बचने सकता है।

क्या यह समझ में आता है - या शायद मैं यहां कुछ दिख रहा हूं? क्या कोई इस समाधान के लिए अन्य विकल्पों का सुझाव दे सकता है?

उत्तर

5

यदि आपने यह प्रतिस्थापन किया है, तो आपके पास अभी भी एक दिए गए इटरेटर का उपयोग करके केवल एक ही धागा होगा। चेतावनी का अर्थ है कि दो धागे को एक ही इटरेटर उदाहरण का उपयोग नहीं करना चाहिए। ऐसा नहीं है कि दो धागे समवर्ती रूप से पुनरावृत्त नहीं हो सकते हैं।

आपकी समस्या यह है कि चूंकि निष्कासन ऑपरेशन को ConcurrentMap के एक परमाणु संचालन में नहीं किया जा सकता है, इसलिए आप एक समवर्ती थ्रेड को मध्यवर्ती स्थिति में मानचित्र देख सकते हैं: एक मान हटा दिया गया है लेकिन नहीं दूसरा वाला।

मुझे यकीन नहीं है कि यह कोई तेज़ होगा, क्योंकि आप कहते हैं कि लेखन प्रदर्शन कोई मुद्दा नहीं है, लेकिन प्रत्येक लिखने पर मानचित्र की प्रति से बचने के लिए आप क्या कर सकते हैं, एक म्यूटेबल की रक्षा करने के लिए ReadWriteLock का उपयोग करना है ConcurrentMap। सभी पढ़ना अभी भी समवर्ती होगा, लेकिन मानचित्र पर एक लेख नक्शे के सभी अन्य धागे को मानचित्र तक पहुंचने से रोक देगा। और जब भी इसे संशोधित किया जाता है तो आपको एक नई प्रतिलिपि बनाने की आवश्यकता नहीं होती है।

2

एक ही समय में एकाधिक iterators/एकाधिक धागे के माध्यम से ConcurrentHashMap को म्यूट करना ठीक है। यह सिर्फ इतना है कि आपको इटरेटर के एक ही उदाहरण को कई धागे तक नहीं सौंपना चाहिए और इसे समवर्ती रूप से उपयोग करना चाहिए।

तो, यदि आप ConcurrentHashMap का उपयोग करते हैं तो आपको वहां synchronized छोड़ने की आवश्यकता नहीं है। जैसा कि जेबी निजेट ने नोट किया है, इस और आपके वर्तमान कार्यान्वयन के बीच का अंतर मध्यवर्ती राज्य की दृश्यता है।यदि आपको इसकी परवाह नहीं है, तो ConcurrentHashMap का उपयोग करना मेरी पसंदीदा पसंद होगी क्योंकि कार्यान्वयन सबसे आसान है और आपको प्रदर्शन पढ़ने या लिखने के बारे में चिंता करने की आवश्यकता नहीं होगी।