java synchronized what is thread synchronization java
यह ट्यूटोरियल जावा में संबंधित अवधारणाओं जैसे जावा लॉक, रेस कंडीशन, म्यूटेक्स, जावा वाष्पशील और डेडलॉक जावा में थ्रेड सिंक्रोनाइज़ेशन की व्याख्या करता है:
एक मल्टीथ्रेडिंग वातावरण में जहां कई थ्रेड्स शामिल होते हैं, एक ही समय में एक ही संसाधन को प्राप्त करने के लिए एक से अधिक थ्रेड का प्रयास करने पर झड़पें होती हैं। इन संघर्षों का परिणाम 'दौड़ की स्थिति' होता है और इस प्रकार यह कार्यक्रम अप्रत्याशित परिणाम उत्पन्न करता है।
उदाहरण के लिए, एक सिंगल फाइल को दो थ्रेड द्वारा अपडेट किया जा रहा है। यदि एक थ्रेड T1 इस फ़ाइल को अपडेट करने की प्रक्रिया में है तो कुछ वैरिएबल कहें। अब जबकि T1 द्वारा यह अपडेट अभी भी जारी है, तो बता दें कि दूसरा थ्रेड T2 भी इसी वेरिएबल को अपडेट करता है। इस तरह चर गलत परिणाम देगा।
=> पूर्ण जावा प्रशिक्षण श्रृंखला यहां देखें।
जब कई थ्रेड्स शामिल होते हैं, तो हमें इन थ्रेड्स को इस तरह से प्रबंधित करना चाहिए कि एक बार में एक थ्रेड द्वारा एक संसाधन तक पहुँचा जा सके। उपरोक्त उदाहरण में, दोनों थ्रेड्स द्वारा एक्सेस की गई फ़ाइल को इस तरह से प्रबंधित किया जाना चाहिए कि T2 उस फ़ाइल तक नहीं पहुंच सकता है जब तक कि T1 इसे एक्सेस नहीं करता है।
यह जावा में 'का उपयोग करके किया जाता है' थ्रेड सिंक्रोनाइज़ेशन ”।
आप क्या सीखेंगे:
- थ्रेड सिंक्रोनाइज़ेशन जावा में
- बिना सिंक्रोनाइज़ेशन के मल्टी-थ्रेडिंग
- सिंक्रनाइज़ेशन के साथ मल्टी-थ्रेडिंग
- निष्कर्ष
थ्रेड सिंक्रोनाइज़ेशन जावा में
जैसा कि जावा एक बहु-भाषा भाषा है, जावा में थ्रेड सिंक्रोनाइज़ेशन का बहुत अधिक महत्व है क्योंकि एक एप्लीकेशन में कई थ्रेड्स समानांतर में निष्पादित होते हैं।
हम कीवर्ड का उपयोग करते हैं 'सिंक्रनाइज़' तथा 'अस्थिर' जावा में सिंक्रोनाइज़ेशन प्राप्त करने के लिए
साझा किए गए ऑब्जेक्ट या संसाधन के परिवर्तनशील होने पर हमें सिंक्रनाइज़ेशन की आवश्यकता होती है। यदि संसाधन अपरिवर्तनीय है, तो सूत्र केवल संसाधन को समवर्ती या व्यक्तिगत रूप से पढ़ेंगे।
इस मामले में, हमें संसाधन को सिंक्रनाइज़ करने की आवश्यकता नहीं है। इस मामले में, जेवीएम यह सुनिश्चित करता है जावा सिंक्रनाइज़ कोड को एक समय में एक थ्रेड द्वारा निष्पादित किया जाता है ।
अधिकांश समय, जावा में साझा संसाधनों का समवर्ती उपयोग 'मेमोरी असंगतता' और 'थ्रेड इंटरफेरेंस' जैसी त्रुटियों को पेश कर सकता है। इन त्रुटियों से बचने के लिए हमें साझा संसाधनों के सिंक्रनाइज़ेशन के लिए जाने की आवश्यकता है ताकि इन संसाधनों तक पहुंच पारस्परिक रूप से अनन्य हो।
हम एक अवधारणा का उपयोग करते हैं सिंक्रनाइज़ेशन को कार्यान्वित करने के लिए मॉनिटर्स। एक मॉनीटर को एक बार में केवल एक थ्रेड द्वारा एक्सेस किया जा सकता है। जब किसी थ्रेड को लॉक मिलता है, तो, हम कह सकते हैं कि थ्रेड मॉनिटर में प्रवेश कर गया है।
जब किसी विशेष थ्रेड द्वारा एक मॉनीटर तक पहुँचा जा रहा है, तो मॉनीटर बंद है और मॉनीटर में प्रवेश करने की कोशिश कर रहे अन्य सभी थ्रेड को तब तक निलंबित रखा जाता है जब तक कि एक्सेस थ्रेड समाप्त नहीं होता है और लॉक जारी नहीं करता है।
आगे बढ़ते हुए, हम इस ट्यूटोरियल में जावा में सिंक्रोनाइज़ेशन पर विस्तार से चर्चा करेंगे। अब, जावा में सिंक्रोनाइज़ेशन से संबंधित कुछ बुनियादी अवधारणाओं पर चर्चा करते हैं।
जावा में रेस कंडीशन
एक बहुआयामी वातावरण में, जब एक से अधिक थ्रेड एक साथ लिखने के लिए साझा संसाधन तक पहुँचने का प्रयास करते हैं, तो संसाधन तक पहुँचने के लिए कई थ्रेड एक-दूसरे की दौड़ लगाते हैं। यह rise रेस कंडीशन ’को जन्म देता है।
एक बात पर विचार करें कि कोई समस्या नहीं है यदि कई थ्रेड्स केवल पढ़ने के लिए एक साझा संसाधन तक पहुँचने का प्रयास कर रहे हैं। समस्या तब उत्पन्न होती है जब एक ही समय में कई थ्रेड एक ही संसाधन तक पहुँचते हैं।
कार्यक्रम में थ्रेड्स के उचित सिंक्रनाइज़ेशन की कमी के कारण दौड़ की स्थिति होती है। जब हम थ्रेड्स को ठीक से सिंक्रोनाइज़ करते हैं तो ऐसे समय में जब केवल एक थ्रेड ही संसाधन तक पहुँच पाएगा, और दौड़ की स्थिति समाप्त हो जाएगी।
तो हम रेस कंडीशन का पता कैसे लगाते हैं?
दौड़ की स्थिति का पता लगाने का सबसे अच्छा तरीका कोड समीक्षा है। एक प्रोग्रामर के रूप में, हमें संभावित दौड़ की स्थितियों की जांच करने के लिए कोड की पूरी तरह से समीक्षा करनी चाहिए।
जावा में ताले / मॉनिटर्स
हमने पहले ही उल्लेख किया है कि हम सिंक्रनाइज़ेशन को लागू करने के लिए मॉनिटर या ताले का उपयोग करते हैं। मॉनिटर या लॉक एक आंतरिक इकाई है और हर वस्तु के साथ जुड़ा हुआ है। इसलिए जब भी किसी वस्तु को वस्तु तक पहुंचने की आवश्यकता होती है, तो उसे पहले अपनी वस्तु का लॉक या मॉनिटर प्राप्त करना होता है, वस्तु पर काम करना होता है और फिर लॉक को छोड़ना होता है।
जावा में ताले नीचे दिखाए गए हैं:
public class Lock { private boolean isLocked = false; public synchronized void lock() throws InterruptedException { while(isLocked) { wait(); } isLocked = true; } public synchronized void unlock(){ isLocked = false; notify(); } }
जैसा कि ऊपर दिखाया गया है, हमारे पास एक ताला () विधि है जो उदाहरण को लॉक करती है। लॉक () विधि को कॉल करने वाले सभी थ्रेड्स को तब तक ब्लॉक किया जाएगा जब तक कि अनब्लॉक () विधि सेट को झंडे के लिए बंद नहीं किया जाता है और सभी वेटिंग थ्रेड को सूचित करता है।
कुछ संकेत ताले के बारे में याद रखने के लिए:
- जावा में, प्रत्येक ऑब्जेक्ट में एक लॉक या एक मॉनिटर होता है। इस लॉक को एक थ्रेड द्वारा एक्सेस किया जा सकता है।
- एक समय में केवल एक धागा इस मॉनिटर या लॉक को प्राप्त कर सकता है।
- जावा प्रोग्रामिंग लैंग्वेज एक सिंक्रोनाइज़्ड कीवर्ड प्रदान करती है, जो हमें सिंक्रोनाइज़ के रूप में ब्लॉक या मेथड बनाकर थ्रेड्स को सिंक्रोनाइज़ करने की अनुमति देता है।
- साझा किए गए संसाधन जिन्हें थ्रेड्स तक पहुँचने की आवश्यकता है उन्हें इस सिंक्रोनाइज़्ड ब्लॉक / विधि के अंतर्गत रखा गया है।
जावा में म्यूटेक्स
हमने पहले ही चर्चा की थी कि एक बहुस्तरीय वातावरण में, दौड़ की स्थिति तब हो सकती है जब एक से अधिक थ्रेड साझा संसाधनों को एक साथ एक्सेस करने की कोशिश करते हैं और दौड़ की स्थिति में अप्रत्याशित उत्पादन होता है।
साझा संसाधन तक पहुंचने की कोशिश करने वाले कार्यक्रम का हिस्सा कहा जाता है 'महत्वपूर्ण अनुभाग' । दौड़ की स्थिति की घटना से बचने के लिए, महत्वपूर्ण अनुभाग तक पहुंच को सिंक्रनाइज़ करने की आवश्यकता है। इस महत्वपूर्ण अनुभाग को सिंक्रनाइज़ करके, हम यह सुनिश्चित करते हैं कि एक बार में केवल एक धागा महत्वपूर्ण अनुभाग तक पहुंच सकता है।
सिंक्रोनाइज़र का सबसे सरल प्रकार 'म्यूटेक्स' है। म्यूटेक्स यह सुनिश्चित करता है कि किसी भी उदाहरण पर, केवल एक धागा महत्वपूर्ण अनुभाग को निष्पादित कर सकता है।
म्यूटेक्स मॉनिटर या ताले की अवधारणा के समान है जिसकी हमने ऊपर चर्चा की थी। यदि किसी थ्रेड को किसी महत्वपूर्ण खंड तक पहुंचने की आवश्यकता है तो उसे म्यूटेक्स का अधिग्रहण करना होगा। म्यूटेक्स के अधिग्रहण के बाद, थ्रेड महत्वपूर्ण खंड कोड तक पहुंच जाएगा, और जब किया जाता है, तो म्यूटेक्स जारी करेगा।
अन्य धागे जो महत्वपूर्ण खंड तक पहुंचने की प्रतीक्षा कर रहे हैं, इस बीच अवरुद्ध हो जाएंगे। जैसे ही थ्रेड होल्डिंग म्यूटेक्स इसे जारी करता है, एक और धागा महत्वपूर्ण अनुभाग में प्रवेश करेगा।
कैसे जावा में सामान्य प्रकार की एक सरणी बनाने के लिए
जावा में म्यूटेक्स को लागू करने के कई तरीके हैं।
- सिंक्रोनाइज़्ड कीवर्ड का उपयोग करना
- सेमाफोर का उपयोग करना
- ReentrantLock का उपयोग करना
इस ट्यूटोरियल में, हम पहले दृष्टिकोण यानी सिंक्रोनाइज़ेशन पर चर्चा करेंगे। अन्य दो दृष्टिकोण - सेमाफोर और रेंट्रेंटलॉक अगले ट्यूटोरियल में चर्चा की जाएगी जिसमें हम जावा समवर्ती पैकेज पर चर्चा करेंगे।
सिंक्रोनाइज़्ड की-वर्ड
जावा एक कीवर्ड 'सिंक्रोनाइज़्ड' प्रदान करता है जिसका उपयोग किसी प्रोग्राम में क्रिटिकल सेक्शन को चिह्नित करने के लिए किया जा सकता है। महत्वपूर्ण खंड कोड या एक पूर्ण विधि का एक ब्लॉक हो सकता है। इस प्रकार, केवल एक धागा सिंक्रनाइज़ किए गए कीवर्ड द्वारा चिह्नित महत्वपूर्ण अनुभाग तक पहुंच सकता है।
हम सिंक्रोनाइज़्ड कीवर्ड का उपयोग करके किसी एप्लिकेशन के लिए समवर्ती भागों (समवर्ती रूप से निष्पादित) को लिख सकते हैं। हम कोड या ब्लॉक को सिंक्रोनाइज़ करने की विधि द्वारा रेस की स्थितियों से भी छुटकारा पा लेते हैं।
जब हम किसी ब्लॉक या विधि को सिंक्रोनाइज़ करते हैं, तो हम इन संस्थाओं के अंदर साझा संसाधनों को एक साथ उपयोग और इस प्रकार भ्रष्टाचार से बचाते हैं।
सिंक्रोनाइज़ेशन के प्रकार
नीचे दिए गए अनुसार सिंक्रनाइज़ेशन के 2 प्रकार हैं:
# 1) प्रक्रिया तुल्यकालन
प्रोसेस सिंक्रोनाइज़ेशन में कई प्रक्रियाएँ या थ्रेड एक साथ निष्पादित होते हैं। वे अंततः एक ऐसी स्थिति में पहुंच जाते हैं, जहां ये प्रक्रियाएं या धागे कार्रवाई के एक विशिष्ट अनुक्रम के लिए प्रतिबद्ध होते हैं।
# 2) थ्रेड सिंक्रोनाइज़ेशन
थ्रेड सिंक्रोनाइज़ेशन में, एक से अधिक थ्रेड साझा स्थान तक पहुंचने का प्रयास कर रहे हैं। थ्रेड्स को इस तरह से सिंक्रनाइज़ किया जाता है कि साझा किए गए स्थान को एक समय में केवल एक थ्रेड द्वारा एक्सेस किया जाता है।
प्रोसेस सिंक्रोनाइज़ेशन इस ट्यूटोरियल के दायरे से बाहर है। इसलिए हम केवल थ्रेड सिंक्रोनाइज़ेशन पर चर्चा करेंगे।
जावा में, हम इसके साथ सिंक्रनाइज़ कीवर्ड का उपयोग कर सकते हैं:
- कोड का एक ब्लॉक
- एक विधि
उपरोक्त प्रकार पारस्परिक रूप से अनन्य प्रकार के थ्रेड सिंक्रनाइज़ेशन हैं। म्यूचुअल बहिष्करण थ्रेड्स को एक दूसरे के साथ हस्तक्षेप करने से साझा डेटा तक पहुंच रखता है।
दूसरे प्रकार का थ्रेड सिंक्रोनाइज़ेशन 'इंटरट्रूड कम्युनिकेशन' है जो थ्रेड्स के बीच सहयोग पर आधारित है। अंतःविषय संचार इस ट्यूटोरियल के दायरे से बाहर है।
इससे पहले कि हम ब्लॉक और विधियों के सिंक्रोनाइज़ेशन के साथ आगे बढ़ें, एक जावा प्रोग्राम को थ्रेड्स के व्यवहार को प्रदर्शित करने के लिए कार्यान्वित करें, जब कोई सिंक्रोनाइज़ेशन न हो।
बिना सिंक्रोनाइज़ेशन के मल्टी-थ्रेडिंग
निम्न जावा प्रोग्राम में कई थ्रेड्स हैं जो सिंक्रनाइज़ नहीं हैं।
class PrintCount { //method to print the thread counter public void printcounter() { try { for(int i = 5; i > 0; i--) { System.out.println('Counter ==> ' + i ); } } catch (Exception e) { System.out.println('Thread interrupted.'); } } } //thread class class ThreadCounter extends Thread { private Thread t; private String threadName; PrintCount PD; //class constructor for initialization ThreadCounter( String name, PrintCount pd) { threadName = name; PD = pd; } //run method for thread public void run() { PD.printcounter(); System.out.println('Thread ' + threadName + ' exiting.'); } //start method for thread public void start () { System.out.println('Starting ' + threadName ); if (t == null) { t = new Thread (this, threadName); t.start (); } } } public class Main { public static void main(String args()) { PrintCount PD = new PrintCount(); //create two instances of thread class ThreadCounter T1 = new ThreadCounter( 'ThreadCounter_1 ', PD ); ThreadCounter T2 = new ThreadCounter( 'ThreadCounter_2 ', PD ); //start both the threads T1.start(); T2.start(); // wait for threads to end try { T1.join(); T2.join(); } catch ( Exception e) { System.out.println('Interrupted'); } } }
उत्पादन
आउटपुट से, हम देख सकते हैं कि चूंकि थ्रेड्स सिंक्रनाइज़ नहीं हैं, आउटपुट असंगत है। दोनों धागे शुरू होते हैं और फिर वे एक के बाद एक काउंटर प्रदर्शित करते हैं। दोनों धागे अंत में बाहर निकलते हैं।
दिए गए प्रोग्राम से, काउंटर थ्रेड्स प्रदर्शित करने के बाद पहले थ्रेड से बाहर निकलना चाहिए था, और फिर दूसरा थ्रेड काउंटर मान को प्रदर्शित करने के लिए शुरू होना चाहिए था।
अब सिंक्रोनाइज़ेशन के लिए जाने दें और कोड ब्लॉक सिंक्रोनाइज़ेशन के साथ शुरू करें।
सिंक्रोनाइज़्ड कोड ब्लॉक
कोड के ब्लॉक को सिंक्रोनाइज़ करने के लिए एक सिंक्रोनाइज़्ड ब्लॉक का उपयोग किया जाता है। इस ब्लॉक में आमतौर पर कुछ लाइनें होती हैं। सिंक्रोनाइज़्ड ब्लॉक का उपयोग तब किया जाता है जब हम नहीं चाहते कि एक पूरी विधि सिंक्रोनाइज़ हो।
उदाहरण के लिए, हमारे पास कोड की 75 पंक्तियों के साथ एक विधि है। इसमें से एक बार में एक धागे से कोड की केवल 10 लाइनों को निष्पादित करना आवश्यक है। इस मामले में, यदि हम पूरी विधि को सिंक्रनाइज़ करते हैं, तो यह सिस्टम पर बोझ होगा। ऐसी स्थितियों में, हम सिंक्रनाइज़ किए गए ब्लॉक के लिए जाते हैं।
सिंक्रनाइज़ विधि का दायरा हमेशा सिंक्रनाइज़ की गई विधि की तुलना में छोटा होता है। एक सिंक्रनाइज़ विधि एक साझा संसाधन का एक ऑब्जेक्ट लॉक करता है जिसे कई थ्रेड द्वारा उपयोग किया जाना है।
एक सिंक्रनाइज़ किए गए ब्लॉक का सामान्य सिंटैक्स नीचे दिखाया गया है:
synchronized (lock_object){ //synchronized code statements }
यहाँ 'lock_object' एक ऑब्जेक्ट रेफरेंस एक्सप्रेशन है जिस पर लॉक प्राप्त करना है। इसलिए जब भी कोई थ्रेड निष्पादन के लिए ब्लॉक के अंदर सिंक्रनाइज़ किए गए स्टेटमेंट को एक्सेस करना चाहता है, तो उसे ject लॉक_बॉजेक्ट / मॉनिटर पर लॉक हासिल करना होगा।
जैसा कि पहले से ही चर्चा की गई है, सिंक्रनाइज़ कीवर्ड यह सुनिश्चित करता है कि एक बार में केवल एक धागा ही लॉक हासिल कर सकता है और अन्य सभी थ्रेड्स को तब तक इंतजार करना पड़ता है जब तक कि लॉक लॉक खत्म होने तक थ्रेड खत्म हो जाए और लॉक रिलीज हो जाए।
ध्यान दें
- 'NullPointerException' को फेंक दिया जाता है, अगर लॉक किया गया है।
- यदि कोई धागा लॉक को पकड़े हुए भी सोता है, तो लॉक जारी नहीं किया जाता है। अन्य थ्रेड्स इस सो समय के दौरान साझा किए गए ऑब्जेक्ट तक नहीं पहुंच पाएंगे।
अब हम उपरोक्त उदाहरण पेश करेंगे जो पहले से ही थोड़े बदलाव के साथ लागू किया गया था। पहले के कार्यक्रम में, हमने कोड को सिंक्रनाइज़ नहीं किया था। अब हम सिंक्रनाइज़ ब्लॉक का उपयोग करेंगे और आउटपुट की तुलना करेंगे।
सिंक्रनाइज़ेशन के साथ मल्टी-थ्रेडिंग
नीचे दिए गए जावा प्रोग्राम में, हम एक सिंक्रनाइज़ ब्लॉक का उपयोग करते हैं। रन विधि में, हम उन लाइनों के कोड को सिंक्रनाइज़ करते हैं जो प्रत्येक थ्रेड के लिए काउंटर प्रिंट करते हैं।
class PrintCount { //print thread counter public void printCounter() { try { for(int i = 5; i > 0; i--) { System.out.println('Counter ==> ' + i ); } } catch (Exception e) { System.out.println('Thread interrupted.'); } } } //thread class class ThreadCounter extends Thread { private Thread t; private String threadName; PrintCount PD; //class constructor for initialization ThreadCounter( String name, PrintCount pd) { threadName = name; PD = pd; } //run () method for thread with synchronized block public void run() { synchronized(PD) { PD.printCounter(); } System.out.println('Thread ' + threadName + ' exiting.'); } //start () method for thread public void start () { System.out.println('Starting ' + threadName ); if (t == null) { t = new Thread (this, threadName); t.start (); } } } public class Main { public static void main(String args()) { PrintCount PD = new PrintCount(); //create thread instances ThreadCounter T1 = new ThreadCounter( 'Thread_1 ', PD ); ThreadCounter T2 = new ThreadCounter( 'Thread_2 ', PD ); //start both the threads T1.start(); T2.start(); // wait for threads to end try { T1.join(); T2.join(); } catch ( Exception e) { System.out.println('Interrupted'); } } }
उत्पादन
अब सिंक्रनाइज़ ब्लॉक का उपयोग करके इस कार्यक्रम का आउटपुट काफी सुसंगत है। जैसा कि अपेक्षित था, दोनों सूत्र क्रियान्वित होने लगते हैं। पहला धागा समाप्त हो गया काउंटर मान प्रदर्शित करता है और बाहर निकलता है। फिर दूसरा धागा काउंटर मान प्रदर्शित करता है और बाहर निकलता है।
सिंक्रोनाइज़ की हुई विधि
आइए इस खंड में सिंक्रनाइज़ की गई विधि पर चर्चा करें। इससे पहले हमने देखा है कि हम एक छोटे ब्लॉक को कम कोड लाइनों से युक्त एक सिंक्रनाइज़ ब्लॉक के रूप में घोषित कर सकते हैं। यदि हम चाहते हैं कि संपूर्ण फ़ंक्शन को सिंक्रनाइज़ किया जाए, तो हम एक विधि को सिंक्रनाइज़ के रूप में घोषित कर सकते हैं।
जब एक विधि को सिंक्रनाइज़ किया जाता है, तो केवल एक धागा एक बार में एक विधि कॉल करने में सक्षम होगा।
एक सिंक्रनाइज़ विधि लिखने के लिए सामान्य वाक्यविन्यास है:
synchronized method_name (parameters){ //synchronized code }
एक सिंक्रोनाइज़्ड ब्लॉक की तरह, एक सिंक्रोनाइज़ की गई विधि के मामले में, हमें एक lock_object की आवश्यकता होती है, जिसका उपयोग सिंक्रोनाइज़ की गई विधि तक पहुँचने वाले थ्रेड द्वारा किया जाएगा।
सिंक्रनाइज़ विधि के लिए, लॉक ऑब्जेक्ट निम्न में से एक हो सकता है:
- यदि सिंक्रनाइज़ विधि स्थिर है, तो लॉक ऑब्जेक्ट cl .class 'ऑब्जेक्ट द्वारा दिया जाता है।
- एक गैर-स्थैतिक विधि के लिए, लॉक ऑब्जेक्ट को वर्तमान ऑब्जेक्ट यानि method इस 'ऑब्जेक्ट द्वारा दिया जाता है।
सिंक्रनाइज़ कीवर्ड की एक ख़ासियत यह है कि यह फिर से प्रवेश है। इसका मतलब है कि एक सिंक्रनाइज़ की गई विधि उसी लॉक के साथ एक और सिंक्रनाइज़ विधि कह सकती है। इसलिए ताला पकड़े एक धागा एक अलग ताला प्राप्त करने के बिना एक और सिंक्रनाइज़ विधि तक पहुंच सकता है।
नीचे दिए गए उदाहरण का उपयोग करके सिंक्रोनाइज़ की गई विधि का प्रदर्शन किया जाता है।
class NumberClass { //synchronized method to print squares of numbers synchronized void printSquares(int n) throws InterruptedException { //iterate from 1 to given number and print the squares at each iteration for (int i = 1; i <= n; i++) { System.out.println(Thread.currentThread().getName() + ' :: '+ i*i); Thread.sleep(500); } } } public class Main { public static void main(String args()) { final NumberClass number = new NumberClass(); //create thread Runnable thread = new Runnable() { public void run() { try { number.printSquares(3); } catch (InterruptedException e) { e.printStackTrace(); } } }; //start thread instance new Thread(thread, 'Thread One').start(); new Thread(thread, 'Thread Two').start(); } }
उत्पादन
उपरोक्त कार्यक्रम में, हमने एक संख्या के वर्गों को प्रिंट करने के लिए एक सिंक्रनाइज़ विधि का उपयोग किया है। संख्या की ऊपरी सीमा एक तर्क के रूप में विधि को पारित की जाती है। फिर 1 से शुरू होकर, ऊपरी सीमा तक पहुंचने तक प्रत्येक संख्या के वर्ग मुद्रित होते हैं।
मुख्य फ़ंक्शन में, थ्रेड आवृत्ति बनाई जाती है। प्रत्येक थ्रेड इंस्टेंस को वर्गों को प्रिंट करने के लिए एक नंबर दिया जाता है।
जैसा कि ऊपर उल्लेख किया गया है, जब एक विधि को सिंक्रनाइज़ किया जाना स्थिर है, तो लॉक ऑब्जेक्ट क्लास में शामिल होता है न कि ऑब्जेक्ट। इसका मतलब है कि हम क्लास पर लॉक करेंगे न कि ऑब्जेक्ट पर। इसे स्टैटिक सिंक्रोनाइज़ेशन कहा जाता है।
एक और उदाहरण नीचे दिया गया है।
विंडोज़ में json फ़ाइल कैसे खोलें
class Table{ //synchronized static method to print squares of numbers synchronized static void printTable(int n){ for(int i=1;i<=10;i++){ System.out.print(n*i + ' '); try{ Thread.sleep(400); }catch(Exception e){} } System.out.println(); } } //thread class Thread_One class Thread_One extends Thread{ public void run(){ Table.printTable(2); } } //thread class Thread_Two class Thread_Two extends Thread{ public void run(){ Table.printTable(5); } } public class Main{ public static void main(String t()){ //create instances of Thread_One and Thread_Two Thread_One t1=new Thread_One (); Thread_Two t2=new Thread_Two (); //start each thread instance t1.start(); t2.start(); } }
उत्पादन
उपरोक्त कार्यक्रम में, हम संख्याओं के गुणन सारणी को प्रिंट करते हैं। प्रत्येक संख्या जिसकी तालिका मुद्रित की जानी है, विभिन्न थ्रेड क्लास का एक थ्रेड उदाहरण है। इस प्रकार हम 2 और 5 के गुणन सारणी को प्रिंट करते हैं, इसलिए हमारे पास क्रमशः 2 और 5 टेबल को प्रिंट करने के लिए दो वर्गों के थ्रेड_ऑन और थ्रेड_टू हैं।
संक्षेप में, जावा सिंक्रोनाइज़्ड कीवर्ड निम्नलिखित कार्य करता है:
- जावा में सिंक्रनाइज़ किया गया कीवर्ड लॉकिंग मैकेनिज्म प्रदान करके साझा संसाधनों तक पारस्परिक रूप से अनन्य पहुंच की गारंटी देता है। लॉकिंग दौड़ की स्थिति को भी रोकता है।
- सिंक्रनाइज़ कीवर्ड का उपयोग करके, हम कोड में समवर्ती प्रोग्रामिंग त्रुटियों को रोकते हैं।
- जब एक विधि या ब्लॉक को सिंक्रनाइज़ के रूप में घोषित किया जाता है, तो एक धागे को सिंक्रनाइज़ विधि या ब्लॉक में प्रवेश करने के लिए एक विशेष लॉक की आवश्यकता होती है। आवश्यक क्रियाओं को करने के बाद, थ्रेड लॉक को रिलीज़ करता है और लेखन ऑपरेशन को फ्लश करेगा। इस तरह यह असंगतता से संबंधित स्मृति त्रुटियों को समाप्त कर देगा।
जावा में अस्थिर
जावा में एक अस्थिर कीवर्ड का उपयोग कक्षाओं को सुरक्षित बनाने के लिए किया जाता है। हम विभिन्न थ्रेड्स द्वारा परिवर्तनीय मान को संशोधित करने के लिए अस्थिर कीवर्ड का भी उपयोग करते हैं। एक अस्थिर कीवर्ड का उपयोग आदिम प्रकार के साथ-साथ वस्तुओं के साथ एक चर घोषित करने के लिए किया जा सकता है।
कुछ मामलों में, एक अस्थिर कीवर्ड का उपयोग सिंक्रनाइज़ कीवर्ड के लिए एक विकल्प के रूप में किया जाता है, लेकिन ध्यान दें कि यह सिंक्रनाइज़ कीवर्ड का विकल्प नहीं है।
जब एक चर को अस्थिर घोषित किया जाता है, तो इसका मूल्य कभी भी कैश नहीं होता है लेकिन हमेशा मुख्य मेमोरी से पढ़ा जाता है। एक अस्थिर चर आदेश देने और दृश्यता की गारंटी देता है। यद्यपि एक चर को अस्थिर के रूप में घोषित किया जा सकता है, हम वर्गों या विधियों को अस्थिर घोषित नहीं कर सकते।
कोड के निम्नलिखित ब्लॉक पर विचार करें:
class ABC{ static volatile int myvar =10; }
उपरोक्त कोड में, चर myvar स्थिर और अस्थिर है। एक स्थिर चर सभी वर्ग वस्तुओं के बीच साझा किया जाता है। अस्थिर चर हमेशा मुख्य मेमोरी में रहता है और कभी भी कैश नहीं होता है।
इसलिए मुख्य मेमोरी में myvar की केवल एक प्रति होगी और इस मेमोरी पर सभी रीड / राइट एक्शन मुख्य मेमोरी से किए जाएंगे। यदि myvar को अस्थिर नहीं घोषित किया गया था, तो प्रत्येक थ्रेड ऑब्जेक्ट की एक अलग प्रतिलिपि होगी जिसके परिणामस्वरूप असंगतता होगी।
Volatile और Synchronized keywords के बीच कुछ अंतर नीचे सूचीबद्ध हैं।
अस्थिर खोजशब्द | सिंक्रोनाइज़्ड कीवर्ड |
---|---|
अस्थिर कीवर्ड का उपयोग केवल चर के साथ किया जाता है। | सिंक्रनाइज़ किए गए कीवर्ड का उपयोग कोड ब्लॉक और विधियों के साथ किया जाता है। |
एक अस्थिर कीवर्ड प्रतीक्षा के लिए थ्रेड को ब्लॉक नहीं कर सकता है। | सिंक्रनाइज़ कीवर्ड प्रतीक्षा के लिए थ्रेड को ब्लॉक कर सकता है। |
थ्रेड का प्रदर्शन अस्थिरता के साथ बेहतर होता है। | थ्रेड प्रदर्शन कुछ हद तक सिंक्रनाइज़ के साथ घटता है। |
अस्थिर चर मुख्य मेमोरी में रहते हैं। | सिंक्रनाइज़ किए गए निर्माण मुख्य मेमोरी में नहीं रहते हैं। |
अस्थिर एक समय में थ्रेड मेमोरी और मुख्य मेमोरी के बीच एक वैरिएबल को सिंक्रनाइज़ करता है। | सिंक्रोनाइज़्ड कीवर्ड एक ही बार में सभी वेरिएबल्स को सिंक्रोनाइज़ करता है। |
जावा में गतिरोध
हमने देखा है कि हम सिंक्रनाइज़ किए गए कीवर्ड का उपयोग करके कई थ्रेड को सिंक्रनाइज़ कर सकते हैं और प्रोग्राम को थ्रेड-सेफ बना सकते हैं। थ्रेड्स को सिंक्रनाइज़ करके, हम यह सुनिश्चित करते हैं कि कई थ्रेड्स बहु-थ्रेडेड वातावरण में एक साथ निष्पादित होते हैं।
हालांकि, कभी-कभी ऐसी स्थिति होती है जिसमें थ्रेड्स एक साथ कार्य नहीं कर सकते हैं। इसके बजाय, वे अंतहीन इंतजार करते हैं। यह तब होता है जब एक थ्रेड किसी संसाधन पर प्रतीक्षा करता है और उस संसाधन को दूसरे थ्रेड द्वारा अवरोधित किया जाता है।
दूसरा धागा, दूसरी ओर, पहले थ्रेड द्वारा अवरुद्ध संसाधन पर प्रतीक्षा कर रहा है। ऐसी स्थिति जावा में 'गतिरोध' को जन्म देती है।
जावा में गतिरोध को नीचे की छवि का उपयोग करके दिखाया गया है।
जैसा कि हम उपरोक्त आरेख से देख सकते हैं, थ्रेड A ने संसाधन r1 को लॉक कर दिया है और संसाधन r2 की प्रतीक्षा कर रहा है। दूसरी ओर, थ्रेड बी ने संसाधन आर 2 को अवरुद्ध कर दिया है और आर 1 पर प्रतीक्षा कर रहा है।
इस प्रकार थ्रेड्स में से कोई भी अपने निष्पादन को समाप्त नहीं कर सकता है जब तक कि वे लंबित संसाधनों को पकड़ न लें। इस स्थिति के परिणामस्वरूप गतिरोध पैदा हो गया है जहां दोनों धागे संसाधनों के लिए अंतहीन इंतजार कर रहे हैं।
नीचे दिए गए जावा में डेडलॉक का एक उदाहरण है।
public class Main { public static void main(String() args) { //define shared resources final String shared_res1 = 'Java tutorials'; final String shared_res2 = 'Multithreading'; // thread_one => locks shared_res1 then shared_res2 Thread thread_one = new Thread() { public void run() { synchronized (shared_res1) { System.out.println('Thread one: locked shared resource 1'); try { Thread.sleep(100);} catch (Exception e) {} synchronized (shared_res2) { System.out.println('Thread one: locked shared resource 2'); } } } }; // thread_two=> locks shared_res2 then shared_res1 Thread thread_two = new Thread() { public void run() { synchronized (shared_res2) { System.out.println('Thread two: locked shared resource 2'); try { Thread.sleep(100);} catch (Exception e) {} synchronized (shared_res1) { System.out.println('Thread two: locked shared resource 1'); } } } }; //start both the threads thread_one.start(); thread_two.start(); } }
उत्पादन
उपरोक्त कार्यक्रम में, हमारे पास दो साझा संसाधन और दो धागे हैं। दोनों धागे एक-एक करके साझा संसाधनों तक पहुँचने की कोशिश करते हैं। आउटपुट दोनों थ्रेड को एक संसाधन को लॉक करते हुए दिखाता है, जबकि अन्य की प्रतीक्षा कर रहा है। जिससे गतिरोध की स्थिति निर्मित होती है।
हालाँकि हम गतिरोध की स्थितियों को पूरी तरह से होने से नहीं रोक सकते, लेकिन हम कुछ कदम उठाकर निश्चित रूप से उनसे बच सकते हैं।
नीचे सूचीबद्ध ऐसे साधन हैं जिनके उपयोग से हम जावा में गतिरोध से बच सकते हैं।
# 1) नेस्टेड तालों से बचकर
नेस्टेड लॉक होने का गतिरोध होने का सबसे महत्वपूर्ण कारण है। नेस्टेड लॉक वे ताले हैं जो कई थ्रेड्स को दिए जाते हैं। इस प्रकार हमें एक से अधिक धागे को ताले देने से बचना चाहिए।
# 2) थ्रेड जॉइन का उपयोग करें
हमें अधिकतम समय के साथ थ्रेड का उपयोग करना चाहिए ताकि थ्रेड्स निष्पादन के लिए अधिकतम समय का उपयोग कर सकें। यह गतिरोध को रोकेगा जो ज्यादातर एक धागे के रूप में होता है जो लगातार दूसरों की प्रतीक्षा करता है।
# 3) अनावश्यक लॉक से बचें
हमें केवल आवश्यक कोड लॉक करना चाहिए। कोड के लिए अनावश्यक ताले होने से कार्यक्रम में गतिरोध पैदा हो सकता है। जैसा कि गतिरोध कोड को तोड़ सकता है और कार्यक्रम के प्रवाह में बाधा डाल सकता है, जिसे हमें अपने कार्यक्रमों में गतिरोध से बचने के लिए इच्छुक होना चाहिए।
बार बार पूछे जाने वाले प्रश्न
Q # 1) सिंक्रोनाइज़ेशन क्या है और यह महत्वपूर्ण क्यों है?
उत्तर: सिंक्रोनाइज़ेशन एक साझा संसाधन की पहुंच को कई थ्रेड्स तक नियंत्रित करने की प्रक्रिया है। सिंक्रनाइज़ेशन के बिना, एक ही समय में कई थ्रेड्स साझा किए गए संसाधन को अद्यतन या परिवर्तित कर सकते हैं जिसके परिणामस्वरूप विसंगतियां हो सकती हैं।
इस प्रकार हमें यह सुनिश्चित करना चाहिए कि एक बहु-थ्रेडेड वातावरण में, थ्रेड्स को सिंक्रनाइज़ किया जाता है ताकि जिस तरह से वे साझा संसाधनों तक पहुंचते हैं वह पारस्परिक रूप से अनन्य और सुसंगत हो।
Q # 2) जावा में सिंक्रोनाइज़ेशन और नॉन-सिंक्रोनाइज़ेशन क्या है?
उत्तर: सिंक्रोनाइज़ेशन का अर्थ है एक निर्माण एक धागा-सुरक्षित है। इसका अर्थ है कि एक साथ कई थ्रेड कंस्ट्रक्शन (कोड ब्लॉक, मेथड, आदि) तक नहीं पहुंच सकते।
गैर-सिंक्रनाइज़ किए गए निर्माण थ्रेड-सुरक्षित नहीं हैं। एकाधिक धागे किसी भी समय गैर-सिंक्रनाइज़ किए गए तरीकों या ब्लॉकों तक पहुंच सकते हैं। जावा में एक लोकप्रिय गैर-सिंक्रनाइज़ क्लास StringBuilder है।
क्यू # 3) सिंक्रनाइज़ेशन की आवश्यकता क्यों है?
उत्तर: जब प्रक्रियाओं को समवर्ती रूप से निष्पादित करने की आवश्यकता होती है, तो हमें सिंक्रनाइज़ेशन की आवश्यकता होती है। ऐसा इसलिए है क्योंकि हमें ऐसे संसाधन चाहिए जो कई प्रक्रियाओं के बीच साझा किए जा सकें।
साझा संसाधनों तक पहुँचने के लिए प्रक्रियाओं या थ्रेड्स के बीच झड़पों से बचने के लिए, हमें इन संसाधनों को सिंक्रनाइज़ करने की आवश्यकता है ताकि सभी थ्रेड्स संसाधनों तक पहुँच प्राप्त करें और अनुप्रयोग भी सुचारू रूप से चले।
Q # 4) आपको सिंक्रोनाइज़्ड ArrayList कैसे मिलता है?
उत्तर: हम ArrayList को ArrayList से सिंक्रोनाइज़ सूची में बदलने के तर्क के रूप में ArrayList के साथ Collections.synchronized List Method का उपयोग कर सकते हैं।
Q # 5) क्या हैशपून सिंक्रोनाइज़ किया गया है?
शीर्ष c ++ साक्षात्कार प्रश्न
उत्तर: नहीं, HashMap सिंक्रनाइज़ नहीं है, लेकिन HashTable सिंक्रनाइज़ है।
निष्कर्ष
इस ट्यूटोरियल में, हमने थ्रेड्स के सिंक्रोनाइज़ेशन पर विस्तार से चर्चा की है। इसके साथ, हमने जावा में अस्थिर कीवर्ड और गतिरोध के बारे में भी जाना। सिंक्रोनाइज़ेशन में प्रोसेस और थ्रेड सिंक्रोनाइज़ेशन होते हैं।
एक बहुस्तरीय वातावरण में, हम थ्रेड सिंक्रोनाइज़ेशन से अधिक चिंतित हैं। हमने यहाँ थ्रेड सिंक्रोनाइज़ेशन का सिंक्रोनाइज़्ड कीवर्ड अप्रोच देखा है।
डेडलॉक एक ऐसी स्थिति है जिसमें कई सूत्र संसाधनों की अंतहीन प्रतीक्षा करते हैं। हमने जावा में डेडलॉक से बचने के तरीकों के साथ-साथ जावा में डेडलॉक का उदाहरण देखा है।
=> स्क्रैच से जावा जानने के लिए यहां जाएं।
अनुशंसित पाठ
- थ्रेड स्लीप () - थ्रेड स्लीप () विधि जावा में उदाहरण के साथ
- जावा थ्रेड्स विथ मेथड्स एंड लाइफ साइकल
- जावा बेसिक्स: जावा सिंटैक्स, जावा क्लास और कोर जावा कॉन्सेप्ट
- जावा में मल्टीथ्रेडिंग - उदाहरण के साथ ट्यूटोरियल
- उदाहरणों के साथ C ++ में गुणा करना
- जावा ट्यूटोरियल फॉर बिगिनर्स: 100+ हैंड्स-ऑन जावा वीडियो ट्यूटोरियल
- जावा कंपोनेंट्स: जावा प्लेटफॉर्म, जेडीके, जेआरई, और जावा वर्चुअल मशीन
- जावा स्ट्रिंग ट्यूटोरियल | उदाहरण के साथ जावा स्ट्रिंग के तरीके