यहाँ एक स्पष्टीकरण और उदाहरण दिया गया है कि यह कैसे पूरा होता है। मुझे बताएं कि क्या ऐसे हिस्से हैं जो स्पष्ट नहीं हैं।
स्रोत के साथ देते हैं
यूनिवर्सल
प्रारंभ:
थ्रेड इंडेक्स को एक परमाणु रूप से बढ़े हुए फैशन में लागू किया जाता है। यह एक AtomicInteger
नाम का उपयोग करके प्रबंधित किया जाता है nextIndex
। इन अनुक्रमों को एक ThreadLocal
उदाहरण के माध्यम से थ्रेड्स को सौंपा गया है जो कि अगले सूचकांक को प्राप्त करने nextIndex
और इसे बढ़ाने के द्वारा खुद को प्रारंभ करता है । ऐसा पहली बार होता है जब प्रत्येक थ्रेड के सूचकांक को पहली बार प्राप्त किया जाता है। ThreadLocal
इस धागे को बनाए गए अंतिम अनुक्रम को ट्रैक करने के लिए A बनाया गया है। यह आरंभिक 0. है। अनुक्रमिक फैक्टरी ऑब्जेक्ट संदर्भ में पारित और संग्रहीत है। AtomicReferenceArray
आकार के दो उदाहरण बनते हैं n
। टेल ऑब्जेक्ट को प्रत्येक संदर्भ को सौंपा गया है, Sequential
कारखाने द्वारा प्रदान की गई प्रारंभिक अवस्था के साथ आरंभीकृत किया गया है । n
अनुमति दी गई थ्रेड्स की अधिकतम संख्या है। इन सरणियों में प्रत्येक तत्व संबंधित थ्रेड इंडेक्स के अंतर्गत आता है।
लागू करने की विधि:
यह वह विधि है जो दिलचस्प काम करती है। यह निम्न कार्य करता है:
- इस मंगलाचरण के लिए एक नया नोड बनाएँ: मेरा
- वर्तमान थ्रेड्स इंडेक्स में ऐलान सरणी में इस नए नोड को सेट करें
फिर अनुक्रमण पाश शुरू होता है। यह तब तक जारी रहेगा जब तक कि वर्तमान आह्वान का अनुक्रम नहीं हो जाता:
- इस थ्रेड द्वारा बनाए गए अंतिम नोड के अनुक्रम का उपयोग करके ऐलान सरणी में एक नोड ढूंढें। इस पर और बाद में।
- यदि चरण 2 में एक नोड पाया जाता है, तो यह अभी तक अनुक्रमित नहीं है, इसके साथ जारी रखें, अन्यथा, बस वर्तमान आह्वान पर ध्यान दें। यह केवल एक दूसरे नोड प्रति आह्वान में मदद करने की कोशिश करेगा।
- चरण 3 में जो भी नोड चुना गया था, उसे अंतिम अनुक्रम नोड के बाद इसे अनुक्रमित करने की कोशिश करते रहें (अन्य धागे हस्तक्षेप कर सकते हैं।) सफलता के बावजूद, वर्तमान थ्रेड्स हेड संदर्भ को अनुक्रम द्वारा लौटाए गए सेट पर सेट करें।
decideNext()
ऊपर वर्णित नेस्टेड लूप की कुंजी decideNext()
विधि है। यह समझने के लिए, हमें नोड वर्ग को देखने की जरूरत है।
नोड वर्ग
यह वर्ग एक डबल-लिंक्ड सूची में नोड्स को निर्दिष्ट करता है। इस वर्ग में बहुत सारी कार्रवाई नहीं है। अधिकांश विधियां सरल पुनर्प्राप्ति विधियां हैं जो काफी आत्म-व्याख्यात्मक होनी चाहिए।
पूंछ विधि
यह 0. के अनुक्रम के साथ एक विशेष नोड उदाहरण देता है। यह बस एक स्थान धारक के रूप में कार्य करता है जब तक कि एक आह्वान इसे प्रतिस्थापित नहीं करता है।
गुण और प्रारंभ
seq
: अनुक्रम संख्या, -1 से आरंभ की गई (जिसका अर्थ नहीं है)
invocation
: के आह्वान का मूल्य apply()
। निर्माण पर सेट करें।
next
: AtomicReference
फॉरवर्ड लिंक के लिए। एक बार असाइन करने के बाद, इसे कभी नहीं बदला जाएगा
previous
: AtomicReference
अनुक्रमण पर और मंजूरी दे दी पिछड़े लिंक के लिएtruncate()
आगे का फैसला
यह विधि गैर-तुच्छ तर्क के साथ नोड में केवल एक है। संक्षेप में, एक नोड को एक उम्मीदवार के रूप में लिंक किए गए सूची में अगले नोड के रूप में पेश किया जाता है। compareAndSet()
अगर यह के संदर्भ रिक्त है विधि की जाँच करेगा और यदि ऐसा है तो, उम्मीदवार के संदर्भ में निर्धारित किया है। यदि संदर्भ पहले से ही सेट है, तो यह कुछ भी नहीं करता है। यह ऑपरेशन परमाणु है इसलिए यदि एक ही समय में दो उम्मीदवारों की पेशकश की जाती है, तो केवल एक का चयन किया जाएगा। यह गारंटी देता है कि केवल एक नोड को अगले एक के रूप में चुना जाएगा। यदि उम्मीदवार नोड चयनित है, तो यह अनुक्रम अगले मान पर सेट है, और यह पिछले लिंक इस नोड पर सेट है।
यूनिवर्सल क्लास में वापस कूदने की विधि लागू करें ...
decideNext()
हमारे नोड या announce
सरणी से नोड के साथ अंतिम अनुक्रमित नोड (जब चेक किया गया) पर कॉल किया जाता है , तो दो संभावित घटनाएं होती हैं: 1. नोड को सफलतापूर्वक अनुक्रमित किया गया था। कुछ अन्य धागे ने इस धागे को पूर्व-खाली किया।
अगला कदम यह जांचना है कि क्या इस मंगलाचरण के लिए नोड बनाया गया है। ऐसा इसलिए हो सकता है क्योंकि इस धागे ने सफलतापूर्वक इसे अनुक्रमित किया या किसी अन्य धागे ने इसे announce
सरणी से उठाया और इसे हमारे लिए अनुक्रमित किया। यदि इसे अनुक्रमित नहीं किया गया है, तो प्रक्रिया को दोहराया जाता है। अन्यथा कॉल इस थ्रेड्स इंडेक्स के लिए ऐलान सरणी को हटाकर और आह्वान के परिणाम मान को वापस करके समाप्त होता है। घोषणा की सरणी को यह सुनिश्चित करने के लिए मंजूरी दे दी गई है कि नोड के आसपास कोई संदर्भ नहीं है जो नोड को कचरा एकत्र करने से रोकेगा और इसलिए उस बिंदु से लिंक सूची में सभी नोड्स को ढेर पर जिंदा रखेगा।
मूल्यांकन विधि
अब जब आह्वान के नोड को सफलतापूर्वक अनुक्रमित किया गया है, तो आह्वान का मूल्यांकन करने की आवश्यकता है। ऐसा करने के लिए, पहला कदम यह सुनिश्चित करना है कि इससे पहले हुए इनवोकेशन का मूल्यांकन किया गया है। यदि वे इस धागे का इंतजार नहीं करेंगे, लेकिन वह काम तुरंत कर देंगे।
सुनिश्चित विधि है
ensurePrior()
विधि जुड़ा हुआ सूची में पिछले नोड की जाँच करके यह काम करता है। यदि यह स्थिति सेट नहीं है, तो पिछले नोड का मूल्यांकन किया जाएगा। नोड कि यह पुनरावर्ती है। यदि पूर्व नोड से पहले के नोड का मूल्यांकन नहीं किया गया है, तो यह उस नोड के लिए मूल्यांकन को आगे बढ़ाएगा।
अब जब पिछले नोड को एक राज्य के रूप में जाना जाता है, तो हम इस नोड का मूल्यांकन कर सकते हैं। अंतिम नोड को पुनर्प्राप्त किया जाता है और स्थानीय चर को सौंपा जाता है। यदि यह संदर्भ शून्य है, तो इसका मतलब है कि कुछ अन्य धागे ने इसे पहले से खाली कर दिया है और पहले से ही इस नोड का मूल्यांकन किया है; यह राज्य है। अन्यथा, Sequential
इस नोड के आह्वान के साथ वस्तु के लागू होने से पहले नोड की स्थिति को पारित किया जाता है । लौटाए गए राज्य को नोड पर सेट किया जाता है और truncate()
विधि को कॉल किया जाता है, नोड से पिछड़े लिंक को साफ करना क्योंकि इसकी अब आवश्यकता नहीं है।
MoveForward विधि
आगे की विधि इस नोड के सभी प्रमुख संदर्भों को स्थानांतरित करने का प्रयास करेगी यदि वे पहले से ही कुछ आगे की ओर इशारा नहीं कर रहे हैं। यह सुनिश्चित करने के लिए है कि यदि कोई थ्रेड कॉल करना बंद कर देता है, तो यह हेड एक नोड के संदर्भ को बनाए नहीं रखेगा, जिसकी अब आवश्यकता नहीं है। compareAndSet()
विधि यकीन है कि हम केवल नोड अद्यतन करता है, तो कुछ अन्य धागा यह बाद से यह प्राप्त की गई थी नहीं बदला है कर देगा।
ऐरे और मदद की घोषणा करें
केवल लॉक-फ़्री के विपरीत इस दृष्टिकोण को प्रतीक्षा-मुक्त बनाने की कुंजी यह है कि हम यह नहीं मान सकते कि थ्रेड शेड्यूलर प्रत्येक थ्रेड को प्राथमिकता देगा जब उसे इसकी आवश्यकता होगी। यदि प्रत्येक धागे को केवल अनुक्रम करने का प्रयास किया जाता है, तो यह स्वयं नोड्स है, यह संभव है कि लोड के तहत एक धागा लगातार पूर्व-खाली किया जा सकता है। इस संभावना को ध्यान में रखते हुए, प्रत्येक थ्रेड पहले अन्य थ्रेड्स को 'मदद' करने का प्रयास करेगा, जो क्रमबद्ध होने में असमर्थ हो सकते हैं।
मूल विचार यह है कि जैसा कि प्रत्येक थ्रेड सफलतापूर्वक नोड बनाता है, असाइन किए गए अनुक्रम मोनोटोनिक रूप से बढ़ रहे हैं। यदि एक थ्रेड या थ्रेड लगातार दूसरे थ्रेड को खाली कर रहे हैं, तो इंडेक्स को announce
सरणी में अनियोजित नोड्स खोजने के लिए उपयोग आगे बढ़ेगा। यहां तक कि अगर प्रत्येक थ्रेड जो वर्तमान में दिए गए नोड को अनुक्रमित करने की कोशिश कर रहा है, तो लगातार दूसरे धागे द्वारा पूर्व-खाली किया जाता है, अंततः सभी थ्रेड्स उस नोड को अनुक्रमित करने की कोशिश करेंगे। उदाहरण के लिए, हम तीन थ्रेड्स के साथ एक उदाहरण का निर्माण करेंगे।
प्रारंभिक बिंदु पर, सभी तीन थ्रेड्स सिर और घोषणा तत्व tail
नोड पर इंगित किए जाते हैं । lastSequence
प्रत्येक थ्रेड के लिए 0 है।
इस बिंदु पर, थ्रेड 1 को एक आह्वान के साथ निष्पादित किया जाता है। यह अंतिम अनुक्रम (शून्य) के लिए घोषणा सरणी की जांच करता है जो कि नोड है जिसे वर्तमान में सूचकांक में निर्धारित किया गया है। यह नोड को अनुक्रमित करता है और इसे lastSequence
1 पर सेट किया जाता है।
थ्रेड 2 को अब एक आह्वान के साथ निष्पादित किया जाता है, यह अंतिम अनुक्रम (शून्य) पर ऐलान सरणी की जांच करता है और देखता है कि इसे मदद की आवश्यकता नहीं है और इसलिए यह आह्वान करने के लिए अनुक्रम का प्रयास करता है। यह सफल होता है और अब यह lastSequence
2 पर सेट है।
थ्रेड 3 को अब निष्पादित किया गया है और यह भी देखता है कि नोड announce[0]
पहले से ही अनुक्रमित है और अनुक्रमों में यह स्वयं का आह्वान है। यह lastSequence
अब 3 पर सेट है।
अब थ्रेड 1 को फिर से लाया जाता है। यह इंडेक्स 1 में घोषणा सरणी की जांच करता है और पाता है कि यह पहले से ही अनुक्रमित है। समवर्ती, थ्रेड 2 को लागू किया जाता है। यह इंडेक्स 2 में घोषणा सरणी की जांच करता है और पाता है कि यह पहले से ही अनुक्रमित है। दोनों थ्रेड 1 और थ्रेड 2 अब अपने स्वयं के नोड्स क्रम करने का प्रयास। थ्रेड 2 जीतता है और यह अनुक्रम है। यह lastSequence
4 पर सेट है। इस बीच, थ्रेड तीन को लागू किया गया है। यह सूचकांक को जांचता है lastSequence
(मॉड 3) और पाता है कि नोड का announce[0]
अनुक्रम नहीं किया गया है। थ्रेड 2 को फिर से उसी समय लागू किया जाता है जब थ्रेड 1 चालू होता है। धागा 1एक अप्रयुक्त आह्वान पाता है announce[1]
जिस पर केवल थ्रेड 2 द्वारा निर्मित नोड है । यह थ्रेड 2 के आह्वान को अनुक्रम करने का प्रयास करता है और सफल होता है। थ्रेड 2 पाता है कि यह स्वयं का नोड है announce[1]
और इसे अनुक्रमित किया गया है। यह 5 सेट lastSequence
है। थ्रेड 3 को फिर से लागू किया जाता है और पाया जाता है कि 1 पर रखा गया नोड announce[0]
अभी भी अनुक्रमित नहीं है और ऐसा करने का प्रयास करता है। इस बीच थ्रेड 2 को भी शामिल किया गया है और थ्री-प्री-थ्रेड्स थ्री। यह अनुक्रम नोड है और इसे lastSequence
6 पर सेट करता है ।
खराब धागा 1 । यद्यपि थ्रेड 3 इसे अनुक्रमित करने की कोशिश कर रहा है, लेकिन दोनों थ्रेड्स को लगातार अनुसूचक द्वारा विफल किया गया है। लेकिन इस बिंदु पर। थ्रेड 2 भी अब announce[0]
(6 मॉड 3) की ओर इशारा कर रहा है । सभी तीन धागे एक ही आह्वान का अनुक्रम करने का प्रयास करते हैं। कोई बात नहीं जो धागा सफल होता है, अगले नोड को अनुक्रमित किया जाएगा थ्रेड 1 का प्रतीक्षा मंगलाचरण यानी नोड द्वारा संदर्भित announce[0]
।
यह अपरिहार्य है। थ्रेड्स को पूर्व-खाली होने के लिए, अन्य थ्रेड्स को अनुक्रमण नोड्स होना चाहिए और जैसा कि वे ऐसा करते हैं, वे लगातार अपने lastSequence
आगे बढ़ेंगे । यदि किसी दिए गए थ्रेड के नोड को लगातार अनुक्रमित नहीं किया जाता है, तो अंततः सभी थ्रेड्स ऐलान सरणी में इसे इंडेक्स करने के लिए इशारा करेंगे। कोई भी धागा कुछ और नहीं करेगा जब तक कि नोड मदद करने की कोशिश नहीं कर रहा है उसे अनुक्रमित किया गया है, सबसे खराब स्थिति यह है कि सभी थ्रेड्स एक ही अप्रयुक्त नोड को इंगित कर रहे हैं। इसलिए, किसी भी आह्वान के अनुक्रम के लिए आवश्यक समय थ्रेड्स की संख्या का एक फ़ंक्शन है और इनपुट का आकार नहीं।