java - जावा में विभिन्न प्रकार के थ्रेड-सुरक्षित सेट्स





concurrency set (4)


1) CopyOnWriteArraySet एक बहुत ही सरल कार्यान्वयन है - मूल रूप से इसमें सरणी में तत्वों की एक सूची होती है, और सूची बदलने पर, यह सरणी की प्रतिलिपि बनाता है। इटरेशन और अन्य एक्सेस जो इस समय चल रहे हैं, पुरानी सरणी के साथ जारी है, पाठकों और लेखकों के बीच सिंक्रनाइज़ेशन की आवश्यकता से परहेज करते हैं (हालांकि खुद को लिखने के लिए सिंक्रनाइज़ करने की आवश्यकता है)। सामान्य रूप से तेज़ सेट ऑपरेशंस (विशेष रूप से contains() ) यहां काफी धीमे होते हैं, क्योंकि सरणी रैखिक समय में खोजी जाएगी।

इसे केवल छोटे सेटों के लिए उपयोग करें जो अक्सर पढ़ा जाएगा (पुनरावृत्त) और शायद ही कभी बदल दिया जाएगा। (स्विंग श्रोता-सेट एक उदाहरण होगा, लेकिन ये वास्तव में सेट नहीं होते हैं, और केवल ईडीटी से ही इस्तेमाल किया जाना चाहिए।)

2) Collections.synchronizedSet सिंक्रनाइज़सेटसेट मूल सेट की प्रत्येक विधि के चारों ओर सिंक्रनाइज़-ब्लॉक को बस लपेटेगा। आपको सीधे मूल सेट तक नहीं पहुंचना चाहिए। इसका मतलब है कि सेट के दो तरीकों को समवर्ती रूप से निष्पादित नहीं किया जा सकता है (कोई अन्य खत्म होने तक अवरुद्ध होगा) - यह थ्रेड-सुरक्षित है, लेकिन यदि एकाधिक थ्रेड वास्तव में सेट का उपयोग कर रहे हैं तो आपके पास समरूपता नहीं होगी। यदि आप इटरेटर का उपयोग करते हैं, तो आपको इटरेटर कॉल के बीच सेट को संशोधित करते समय आमतौर पर ConcurrentModificationExceptions से बचने के लिए बाहरी रूप से सिंक्रनाइज़ करने की आवश्यकता होती है। प्रदर्शन मूल सेट के प्रदर्शन की तरह होगा (लेकिन कुछ सिंक्रनाइज़ेशन ओवरहेड के साथ, और एक साथ उपयोग किए जाने पर अवरुद्ध)।

इसका उपयोग करें यदि आपके पास केवल कम सहमति है, और यह सुनिश्चित करना चाहते हैं कि सभी परिवर्तन तुरंत अन्य धागे पर दिखाई दे रहे हों।

3) ConcurrentSkipListSet SortedSet समवर्ती SortedSet कार्यान्वयन है, जिसमें ओ (लॉग एन) में अधिकांश बुनियादी परिचालन होते हैं। यह समवर्ती जोड़ने / निकालने और पढ़ने / पुनरावृत्ति की अनुमति देता है, जहां पुनरावृत्त इटरेटर बनने के बाद परिवर्तनों के बारे में बता सकता है या नहीं। थोक संचालन केवल एकाधिक एकल कॉल होते हैं, और परमाणु रूप से नहीं - अन्य धागे केवल उनमें से कुछ का निरीक्षण कर सकते हैं।

जाहिर है आप इसका उपयोग केवल तभी कर सकते हैं जब आपके तत्वों पर आपका कुल आदेश हो। यह उच्च-समेकन स्थितियों के लिए एक आदर्श उम्मीदवार की तरह दिखता है, बहुत बड़े सेट के लिए (ओ (लॉग एन) की वजह से)।

4) ConcurrentHashMap (और इससे प्राप्त सेट) के लिए: यहां सबसे बुनियादी विकल्प हैं (औसतन, यदि आपके पास ओ (1) में एक अच्छा और तेज़ hashCode() ) है (लेकिन ओ (एन) में गिरावट हो सकती है) हैश मैप / हैशसेट के लिए पसंद है। लेखन के लिए एक सीमित समरूपता है (तालिका विभाजित है, और लिखने का उपयोग आवश्यक विभाजन पर सिंक्रनाइज़ किया जाएगा), जबकि पढ़ने का उपयोग स्वयं और लेखन धागे से पूरी तरह से समवर्ती है (लेकिन हो सकता है कि वर्तमान में होने वाले परिवर्तनों के नतीजे न हों लिखा हुआ)। इटेटरेटर इसे बनाए जाने के बाद परिवर्तन देख सकता है या नहीं भी हो सकता है, और थोक संचालन परमाणु नहीं हैं। आकार बदलना धीमा है (हैश मैप / हैशसेट के लिए), इस प्रकार सृजन पर आवश्यक आकार का आकलन करके और इसके बारे में 1/3 अधिक का उपयोग करके इसे टालने का प्रयास करें, क्योंकि यह 3/4 पूर्ण होने पर आकार बदलता है)।

इसका उपयोग तब करें जब आपके पास बड़े सेट हों, एक अच्छा (और तेज़) हैश फ़ंक्शन और नक्शा बनाने से पहले सेट आकार और आवश्यक समरूपता का अनुमान लगाया जा सके।

5) क्या कोई अन्य समवर्ती मानचित्र कार्यान्वयन यहां उपयोग कर सकता है?

जावा में थ्रेड-सुरक्षित सेट जेनरेट करने के कई सारे कार्यान्वयन और तरीके प्रतीत होते हैं। कुछ उदाहरणों में शामिल हैं

1) CopyOnWriteArraySet

2) संग्रह। सिंक्रनाइज़सेटसेट (सेट सेट)

3) ConcurrentSkipListSet

4) संग्रह। NewewetFromMap (नया ConcurrentHashMap ())

5) अन्य सेट्स इसी तरह से उत्पन्न होते हैं (4)

ये उदाहरण Concurrency पैटर्न से आते हैं : जावा 6 में समवर्ती सेट कार्यान्वयन

क्या कोई इन उदाहरणों और दूसरों के मतभेदों, फायदों और नुकसान को आसानी से समझा सकता है? मुझे जावा स्टड डॉक्स से सीधे सब कुछ समझने और रखने में परेशानी हो रही है।




यदि जावाडॉक्स मदद नहीं करते हैं, तो आपको शायद डेटा संरचनाओं के बारे में पढ़ने के लिए एक पुस्तक या आलेख मिलना चाहिए। एक नजर में:

  • CopyOnWriteArraySet प्रत्येक बार जब आप संग्रह को म्यूटेट करते हैं तो अंतर्निहित सरणी की एक नई प्रति बनाता है, इसलिए लिखने धीमे होते हैं और इटरेटर तेज़ और सुसंगत होते हैं।
  • Collections.synchronizedSet () सेट थ्रेडसेफ बनाने के लिए पुरानी-स्कूल सिंक्रनाइज़ विधि कॉल का उपयोग करता है। यह एक कम प्रदर्शन वाला संस्करण होगा।
  • ConcurrentSkipListSet निष्पादक बैच ऑपरेशंस (addAll, removeAll, आदि) और Iterators के साथ कलाकार लिखता है।
  • Collections.newSetFromMap (नया ConcurrentHashMap ()) में ConcurrentHashMap का अर्थशास्त्र है, जो मुझे विश्वास है कि पढ़ने या लिखने के लिए जरूरी नहीं है, लेकिन ConcurrentSkipListSet की तरह, असंगत बैच ऑपरेशंस है।



AtomicReference<Set> और प्रत्येक संशोधन पर पूरे सेट को प्रतिस्थापित करके CopyOnWriteArraySet की समवर्ती-संबंधित गुणों के साथ CopyOnWriteArraySet के contains() प्रदर्शन को जोड़ना संभव है।

कार्यान्वयन स्केच:

public abstract class CopyOnWriteSet<E> implements Set<E> {

    private final AtomicReference<Set<E>> ref;

    protected CopyOnWriteSet( Collection<? extends E> c ) {
        ref = new AtomicReference<Set<E>>( new HashSet<E>( c ) );
    }

    @Override
    public boolean contains( Object o ) {
        return ref.get().contains( o );
    }

    @Override
    public boolean add( E e ) {
        while ( true ) {
            Set<E> current = ref.get();
            if ( current.contains( e ) ) {
                return false;
            }
            Set<E> modified = new HashSet<E>( current );
            modified.add( e );
            if ( ref.compareAndSet( current, modified ) ) {
                return true;
            }
        }
    }

    @Override
    public boolean remove( Object o ) {
        while ( true ) {
            Set<E> current = ref.get();
            if ( !current.contains( o ) ) {
                return false;
            }
            Set<E> modified = new HashSet<E>( current );
            modified.remove( o );
            if ( ref.compareAndSet( current, modified ) ) {
                return true;
            }
        }
    }

}



जब तक वे समाप्त नहीं होते हैं तब तक थ्रेड एकत्र नहीं किए जाते हैं। वे कचरा संग्रह की roots के रूप में काम करते हैं । वे उन कुछ वस्तुओं में से एक हैं जिन्हें उनके बारे में भूलकर या संदर्भों को समाशोधन करके पुनः प्राप्त नहीं किया जाएगा।

विचार करें: एक कार्यकर्ता थ्रेड को समाप्त करने के लिए मूल पैटर्न थ्रेड द्वारा देखी गई कुछ स्थिति चर सेट करना है। धागा आवधिक रूप से चर की जांच कर सकता है और इसे समाप्त करने के संकेत के रूप में उपयोग कर सकता है। यदि चर घोषित नहीं किया गया है volatile, तो चर में परिवर्तन थ्रेड द्वारा नहीं देखा जा सकता है, इसलिए इसे समाप्त नहीं किया जाएगा। या कल्पना करें कि कुछ धागे किसी साझा ऑब्जेक्ट को अपडेट करना चाहते हैं, लेकिन इसे लॉक करने का प्रयास करते समय डेडलॉक।

यदि आपके पास केवल कुछ हद तक धागे हैं तो ये बग शायद स्पष्ट होंगे क्योंकि आपका प्रोग्राम ठीक से काम करना बंद कर देगा। यदि आपके पास एक थ्रेड पूल है जो आवश्यकतानुसार अधिक धागे बनाता है, तो अप्रचलित / फंसे धागे पर ध्यान नहीं दिया जा सकता है, और अनिश्चित काल तक जमा हो जाएगा, जिससे स्मृति रिसाव हो सकता है। थ्रेड आपके आवेदन में अन्य डेटा का उपयोग करने की संभावना है, इसलिए जो कुछ भी वे सीधे एकत्र किए जाने से सीधे संदर्भित करते हैं, उन्हें भी रोक देंगे।

एक खिलौना उदाहरण के रूप में:

static void leakMe(final Object object) {
    new Thread() {
        public void run() {
            Object o = object;
            for (;;) {
                try {
                    sleep(Long.MAX_VALUE);
                } catch (InterruptedException e) {}
            }
        }
    }.start();
}

System.gc()आप जो भी पसंद करते हैं उसे कॉल करें , लेकिन पारित वस्तु leakMeकभी मर जाएगी नहीं।

(* संपादित *)







java concurrency set