concurrency java semaphore
यह ट्यूटोरियल जावा में कॉनएफ़र को कार्यान्वित करने के लिए जावा सेमाफोर, एक्ज़ीक्यूटर फ्रेमवर्क, एक्ज़ीक्यूटर सर्विस जैसे java.util.concurrent पैकेज के घटकों पर चर्चा करेगा:
हमारे पिछले जावा ट्यूटोरियल से, हम जानते हैं कि जावा प्लेटफॉर्म जमीन से समवर्ती प्रोग्रामिंग का समर्थन करता है। संगामिति की मूल इकाई एक सूत्र है और हमने जावा में थ्रेड्स और मल्टीथ्रेडिंग पर विस्तार से चर्चा की है।
जावा 5 से, जावा प्लेटफॉर्म में 'java.util.concurrent' नामक एक पैकेज जोड़ा गया था। इस पैकेज में कक्षाओं और पुस्तकालयों का एक सेट है जो प्रोग्रामर के लिए समवर्ती (बहु-थ्रेडेड) अनुप्रयोगों को विकसित करना आसान बनाता है। इस पैकेज का उपयोग करते हुए, हमें जटिल कक्षाएं नहीं लिखनी हैं क्योंकि हमारे पास अधिकांश समवर्ती अवधारणाओं का कार्यान्वयन है।
=> यहाँ सभी जावा ट्यूटोरियल की जाँच करें।
इस ट्यूटोरियल में, हम java.util.concurrent पैकेज के विभिन्न घटकों के बारे में चर्चा करेंगे जो कि जावा में संगामिति और मल्टीथ्रेडिंग से संबंधित हैं।
आप क्या सीखेंगे:
java.util.concurrent पैकेज
नीचे सूचीबद्ध java.util.concurrent पैकेज के विभिन्न घटक हैं जो जावा में संगामिति और मल्टीथ्रेडिंग से संबंधित हैं। आइए सरल प्रोग्रामिंग उदाहरणों की मदद से प्रत्येक घटक को विस्तार से देखें। कुछ घटक हम करेंगे
चर्चा कर रहे हैं:
- यययययय यययय ययययय यययय यययय यययय यययय यययय यययय यययय यययय यययय यययय यययय यययय यययय यययय यययय ययययय यययय ययययय यययय यययय ययययय ययययय यययय यययय ययययययययययययययययययययययययययययययययययययययययययययययययययययययययययययययययययययययययययययययययययययययय)))) एि)))) यि)))))))) िचक)) के पास))))) के यिच)) से))) के यिच)) से) ्)) ्)) ्)) ं) ्)) ं) ्) ्) ्) or
- एक्ज़ीक्यूशन सर्विस
- धागा पूल
- प्रतिदेय
- ताले- रेंट्रेंटलॉक
- सिकंदरा
- ForkJoinPool
एक्ज़ीक्यूटर फ्रेमवर्क जावा में
जावा में एक्ज़ीक्यूटर फ्रेमवर्क को JDK 5 रिलीज़ के साथ रिलीज़ किया गया था। निष्पादन ढाँचा (java.util.concurrent.Executor) एक ढाँचा है जिसमें ऐसे घटक होते हैं जो हमें कई धागों को कुशलता से संभालने में मदद करते हैं।
एक्ज़ीक्यूटर फ्रेमवर्क का उपयोग करके, हम उन वस्तुओं को चला सकते हैं जो पहले से मौजूद थ्रेड्स का पुन: उपयोग करके रनने योग्य हैं। जब हमें वस्तुओं को चलाने की आवश्यकता होती है तो हमें हर बार नए धागे नहीं बनाने चाहिए।
निष्पादनकर्ता API एक कार्य का उपयोग करके वास्तविक कार्य से निष्पादन को अलग या हटाता है निर्वाहक । निष्पादक इंटरफ़ेस पर एक निष्पादक केंद्रित होता है और इसमें उप-इंटरफेस होते हैं यानी एक्ज़ीक्यूशन सर्विस और वर्ग ThreadPoolExecutor।
इस प्रकार एक्ज़ीक्यूसर का उपयोग करते हुए, हमें बस रन करने योग्य ऑब्जेक्ट्स बनाने हैं और फिर उन्हें निष्पादक को भेजना है जो उन्हें निष्पादित करता है।
एक्ज़ीक्यूटर फ्रेमवर्क का उपयोग करते समय सबसे अच्छी प्रथाओं में से कुछ हैं,
- हमें शीर्ष सूचियों की समीक्षा करने के लिए एक कोड को क्रॉस-चेक करना और योजना बनाना चाहिए ताकि हम कोड में गतिरोध के साथ-साथ लाइवलॉक का पता लगा सकें।
- जावा कोड को हमेशा स्टैटिक एनालिसिस टूल के खिलाफ निष्पादित किया जाना चाहिए। उदाहरण स्थिर विश्लेषण उपकरण FindBugs और PMD हैं।
- हमें न केवल अपवादों को पकड़ना चाहिए, बल्कि बहु-थ्रेडेड कार्यक्रमों में त्रुटियों को भी देखना चाहिए।
अब जावा में एक्ज़ीक्यूटर फ्रेमवर्क के घटकों पर चर्चा करते हैं।
निर्वाहक
निष्पादक को एक इंटरफ़ेस के रूप में परिभाषित किया जा सकता है जो किसी ऑब्जेक्ट का प्रतिनिधित्व करने के लिए उपयोग किया जाता है जो इसे प्रदान किए गए कार्यों को निष्पादित करता है। क्या कार्य वर्तमान या नए धागे पर चलाया जाना है, उस बिंदु पर निर्भर करता है जहां से मंगलाचरण शुरू किया गया था जो आगे कार्यान्वयन पर निर्भर करता है।
इसलिए एक्जिक्यूटर का उपयोग करके, हम वास्तविक कार्य से कार्यों को डी-युगल कर सकते हैं और फिर उन्हें अतुल्यकालिक रूप से चला सकते हैं।
हालांकि, एक्जिक्यूटर का उपयोग करके कार्य का निष्पादन अतुल्यकालिक नहीं है। एक्ज़क्यूटर्स भी थ्रेडिंग थ्रेड का उपयोग करके कार्य को तुरंत लागू कर सकते हैं।
नीचे दिए गए एक्ज़ीक्यूटर उदाहरण बनाने के लिए कोड का एक उदाहरण है:
public class Invoker implements Executor { @Override public void execute (Runnable r_interface) { r_interface.run(); } }
एक बार इनवॉकर बनाने के बाद, जैसा कि ऊपर दिखाया गया है, हम इसका उपयोग कार्य को निष्पादित करने के लिए निम्नानुसार कर सकते हैं।
public void execute () { Executor executor = new Invoker (); executor.execute ( () -> { //perform task }); }
ध्यान दें कि यदि कार्य एक्सेक्यूटर द्वारा स्वीकार नहीं किया जाता है, तो यह RejectExecutionException को फेंकता है।
एक्ज़ीक्यूशन सर्विस
एक ExecutorService (java.util.concurrent.ExecutorService) थ्रेड की उपलब्धता के अनुसार सबमिट किए गए कार्यों को शेड्यूल करता है और एक मेमोरी कतार भी बनाए रखता है। ExecutorService कार्यों के अतुल्यकालिक प्रसंस्करण के लिए एक पूर्ण समाधान के रूप में कार्य करता है।
ExecutorService को कोड में उपयोग करने के लिए, हम एक Runnable क्लास बनाते हैं। ExecutorService एक थ्रेड पूल रखता है और थ्रेड्स को कार्य भी असाइन करता है। धागा उपलब्ध नहीं होने की स्थिति में कार्य भी कतार में लग सकते हैं।
नीचे दिए गए ExecutorService का एक सरल उदाहरण है।
import java.util.concurrent.*; public class Main { public static void main(String() args) { //create ExecutorService instance with 10 threads ExecutorService executor_Service = Executors.newFixedThreadPool(10); //assign the service to Runnable instance executor_Service.execute(new Runnable() { @Override public void run() { //print the message System.out.println('Simple Example of ExecutorService!!!'); } }); //shutdown executorService executor_Service.shutdown(); } }
उत्पादन
उपरोक्त कार्यक्रम में, हम 10 थ्रेड्स वाले थ्रेड पूल के साथ एक सरल एक्सेकॉर्स सर्विस बनाते हैं। फिर इसे रननेबल उदाहरण के लिए सौंपा गया है और उपरोक्त संदेश को प्रिंट करने के लिए निष्पादित किया गया है। संदेश को प्रिंट करने के बाद, ExecutorService बंद हो जाती है।
धागा पूल
जावा में एक थ्रेड पूल कार्यकर्ता थ्रेड्स का एक समूह है जिसे कई बार और नियोजित कार्यों के लिए पुन: उपयोग किया जा सकता है।
थ्रेड पूल में निश्चित आकार के थ्रेड्स का एक समूह होता है। प्रत्येक थ्रेड को थ्रेड पूल से बाहर निकाला जाता है और सेवा प्रदाता द्वारा एक कार्य सौंपा जाता है। नियत कार्य पूरा हो जाने के बाद, धागा थ्रेड पूल को फिर से दिया जाता है।
थ्रेड पूल फायदेमंद है क्योंकि हमें हर बार कार्य उपलब्ध होने पर एक नया धागा बनाने की ज़रूरत नहीं है, जिससे प्रदर्शन बढ़ाया जाता है। इसका उपयोग वास्तविक समय के अनुप्रयोगों में किया जाता है जो सर्वलेट और जेएसपी का उपयोग करते हैं जहां थ्रेड पूल का उपयोग अनुरोधों को संसाधित करने के लिए किया जाता है।
बहु-थ्रेडेड अनुप्रयोगों में, थ्रेड पूल संसाधनों को बचाता है और पूर्वनिर्धारित सीमाओं के भीतर समानता को समाहित करने में मदद करता है।
नीचे जावा प्रोग्राम जावा में थ्रेड पूल प्रदर्शित करता है।
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; class WorkerThreadClass implements Runnable { private String message; //thread class constructor public WorkerThreadClass(String s){ this.message=s; } //run method for thread public void run() { System.out.println(' Start: '+message); processmessage(); //sleep between start and end System.out.println(' End: '+ message); } //processmessage method => sleeps the thread for 2 sec private void processmessage() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } public class Main { public static void main(String() args) { //create a ExecutorService instance ExecutorService executor = Executors.newFixedThreadPool(5);//creating a pool of 5 threads //create thread instances and execute them for (int i = 0; i <5; i++) { Runnable workerThrd = new WorkerThreadClass('Thread_' + i); executor.execute(workerThrd);//calling execute method of ExecutorService } //shutdown ExecutorService executor.shutdown(); while (!executor.isTerminated()) { } System.out.println('Finished all threads'); } }
उत्पादन
उपरोक्त कार्यक्रमों में, 5 थ्रेड्स का एक थ्रेड पूल है जो 'newFixedThreadPool' विधि का उपयोग करके बनाया गया है। फिर थ्रेड्स बनाए जाते हैं और पूल में जोड़ दिए जाते हैं और निष्पादन के लिए एक्सेकॉर्स सर्विस को सौंपा जाता है।
जावा में कॉल करने योग्य
हम पहले से ही जानते हैं कि हम दो तरीकों का उपयोग करके धागे बना सकते हैं। एक दृष्टिकोण थ्रेड वर्ग का विस्तार करके है, जबकि दूसरा दृष्टिकोण एक रननेबल इंटरफ़ेस को लागू करने से है।
हालांकि, रननेबल इंटरफ़ेस का उपयोग करके बनाए गए थ्रेड्स में एक विशेषता का अभाव होता है यानी थ्रेड समाप्त होने या चलाने () के पूरा होने पर यह परिणाम नहीं देता है। यह वह जगह है जहाँ Callable इंटरफ़ेस चित्र में आता है।
एक कॉल करने योग्य इंटरफ़ेस का उपयोग करके हम एक कार्य को परिभाषित करते हैं ताकि यह एक परिणाम लौटाए। यह एक अपवाद भी फेंक सकता है। कॉल करने योग्य इंटरफ़ेस java.util.concurrent पैकेज का एक हिस्सा है।
कॉल करने योग्य इंटरफ़ेस एक कॉल () विधि प्रदान करता है जो रन () विधि के समान लाइनों पर है जो रननेबल इंटरफ़ेस द्वारा प्रदान की गई एकमात्र अंतर है कि कॉल () विधि एक मान लौटाती है और चेक किए गए अपवाद को फेंकता है।
कॉल करने योग्य इंटरफ़ेस की कॉल () विधि में निम्नलिखित प्रोटोटाइप है।
public Object call () throws Exception;
चूंकि कॉल () विधि एक ऑब्जेक्ट लौटाती है, मुख्य सूत्र को इसके बारे में पता होना चाहिए।
इसलिए मुख्य थ्रेड को ज्ञात किसी अन्य ऑब्जेक्ट में रिटर्न वैल्यू संग्रहीत किया जाना चाहिए। यह उद्देश्य 'भविष्य' ऑब्जेक्ट का उपयोग करके किया जाता है। एक भविष्य की वस्तु एक ऐसी वस्तु है जो एक थ्रेड द्वारा लौटाए गए परिणाम को रखती है। या दूसरे शब्दों में, यह कॉलेबल रिटर्न के परिणाम को रोक देगा।
कॉल करने योग्य कार्य किसी अन्य थ्रेड पर चलना चाहिए। एक फ्यूचर ऑब्जेक्ट एक अलग थ्रेड से लौटाए गए रिजल्ट को स्टोर करता है।
एक थ्रेड बनाने के लिए कॉल करने योग्य इंटरफ़ेस का उपयोग नहीं किया जा सकता है। हमें एक धागा बनाने के लिए Runnable की आवश्यकता है। फिर परिणाम को स्टोर करने के लिए फ्यूचर ऑब्जेक्ट की आवश्यकता होती है। Java 'FutureTask' नाम का एक ठोस प्रकार प्रदान करता है जो Runnable और Future दोनों को लागू करके कार्यक्षमता को जोड़ता है।
हम Callable के साथ एक कंस्ट्रक्टर प्रदान करके FutureTask बनाते हैं। फ़्यूचरटैस्क ऑब्जेक्ट को थ्रेड क्लास के कंस्ट्रक्टर को थ्रेड ऑब्जेक्ट बनाने के लिए दिया जाता है।
नीचे दिया गया एक जावा प्रोग्राम है जो कॉल करने योग्य इंटरफ़ेस और फ्यूचर ऑब्जेक्ट को प्रदर्शित करता है। हम इस प्रोग्राम में FutureTask ऑब्जेक्ट का भी उपयोग करते हैं।
जैसा कि पहले ही उल्लेख किया गया है, कार्यक्रम में हम एक वर्ग बनाते हैं जो एक ओवरराइड कॉल () विधि के साथ एक कॉल करने योग्य इंटरफ़ेस को लागू करता है। मुख्य विधि में, हम 10 FutureTask ऑब्जेक्ट बनाते हैं। प्रत्येक ऑब्जेक्ट निर्माता के पास तर्क के रूप में एक कॉल करने योग्य वर्ग ऑब्जेक्ट होता है। फिर FutureTask ऑब्जेक्ट थ्रेड इंस्टेंस के साथ जुड़ा हुआ है।
इसलिए अप्रत्यक्ष रूप से हम एक कॉल करने योग्य इंटरफ़ेस ऑब्जेक्ट का उपयोग करके एक थ्रेड बनाते हैं।
import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; //create a class implementing Callable interface class CallableDemo implements Callable { //define call () method public Object call() throws Exception { Random generator = new Random(); Integer randomNumber = generator.nextInt(10); Thread.sleep(randomNumber * 1000); return randomNumber; } } public class Main { public static void main(String() args) throws Exception { // Array of FutureTask objects FutureTask() randomNumberTasks = new FutureTask(10); for (int i = 0; i <10; i++) { Callable callable = new CallableDemo(); // Create the FutureTask with Callable class randomNumberTasks(i) = new FutureTask(callable); // create thread with FutureTask Thread t = new Thread(randomNumberTasks(i)); //start the thread t.start(); } System.out.println('The contents of FutureTask objects:'); for (int i = 0; i < 10; i++) { // get() contents of FutureTask System.out.print(randomNumberTasks(i).get() + ' '); } } }
उत्पादन
जैसा कि उपरोक्त कार्यक्रम में दिखाया गया है, कॉल करने योग्य (कॉल) विधि जो कॉलेबल को लागू करने वाली कक्षा में ओवरराइड होती है, यादृच्छिक संख्या उत्पन्न करती है। एक बार धागा शुरू होने के बाद, यह इन यादृच्छिक संख्याओं को प्रदर्शित करता है।
इसके अलावा, हम FutureTask वस्तुओं का उपयोग मुख्य कार्य में करते हैं। चूंकि यह भविष्य के इंटरफ़ेस को लागू करता है, हमें थ्रेड ऑब्जेक्ट्स में परिणामों को संग्रहीत करने की आवश्यकता नहीं है। इसी तरह, हम कार्य को रद्द कर सकते हैं, जांच सकते हैं कि यह चल रहा है या पूरा हो गया है, और FutureTask ऑब्जेक्ट का उपयोग करके भी परिणाम प्राप्त करें।
ReentrantLock जावा में
हमने अपने अंतिम ट्यूटोरियल में सिंक्रनाइज़ किए गए कीवर्ड का उपयोग करके थ्रेड सिंक्रोनाइज़ेशन पर चर्चा की है। थ्रेड सिंक्रोनाइज़ेशन के लिए सिंक्रोनाइज़ शब्द का उपयोग मूल विधि है और कुछ हद तक कठोर है।
सिंक्रनाइज़ कीवर्ड का उपयोग करके, एक थ्रेड केवल एक बार लॉक हो सकता है। इसके अलावा, एक थ्रेड सिंक्रनाइज़ ब्लॉक से बाहर निकलने के बाद, अगला थ्रेड लॉक लेता है। कोई प्रतीक्षा कतार नहीं है। इन मुद्दों के कारण कुछ अन्य धागे की भुखमरी हो सकती है क्योंकि इसे लंबे समय तक संसाधनों तक पहुंच नहीं मिल सकती है।
इन मुद्दों को संबोधित करने के लिए, हमें थ्रेड्स को सिंक्रनाइज़ करने की एक लचीली विधि की आवश्यकता है। 'रेंटेंट लॉक्स' जावा में यह विधि है जो अधिक से अधिक लचीलेपन के साथ सिंक्रनाइज़ेशन प्रदान करती है।
वर्ग 'रेअन्ट्रेंटलॉक' रेन्ट्रेंट ताले को लागू करता है और पैकेज का एक हिस्सा है 'आयात java.util.concurrent.locks'। ReentrantLock वर्ग साझा संसाधनों तक पहुंचने के लिए विधि सिंक्रनाइज़ेशन प्रदान करता है। थ्रेड्स द्वारा एक्सेस किए जाने पर लॉकिंग / अनलॉकिंग संसाधनों के लिए क्लासेस में लॉक और अनलॉक तरीके भी होते हैं।
ReentrantLock की एक ख़ासियत यह है कि धागा ReentrantLock का उपयोग करते हुए साझा संसाधन को एक से अधिक बार लॉक कर सकता है। यह होल्ड काउंट प्रदान करता है जो थ्रेड को संसाधन लॉक करने पर एक पर सेट होता है।
थ्रेड अनलॉक करने से पहले संसाधन को फिर से दर्ज और एक्सेस कर सकता है। हर बार जब धागा रेन्ट्रेंट लॉक का उपयोग करते हुए संसाधन तक पहुंचता है, तो पकड़ की गिनती एक से बढ़ जाती है। प्रत्येक अनलॉक के लिए, होल्ड काउंट एक से घटाया जाता है।
जब होल्ड की संख्या 0 तक पहुँच जाती है, तो साझा संसाधन अनलॉक हो जाता है।
रेंट्रेंटलॉक वर्ग एक निष्पक्षता पैरामीटर भी प्रदान करता है जो बूलियन मूल्य है जिसे लॉक के निर्माता के साथ पारित किया जा सकता है। जब फेयरनेस पैरामीटर सही पर सेट होता है, तो जब भी कोई थ्रेड लॉक रिलीज़ करता है, तो लॉक को सबसे लंबे वेटिंग थ्रेड में पास किया जाता है। यह भुखमरी को रोकता है।
रेंटेंट ताले का उपयोग निम्नानुसार किया जा सकता है:
return_type method_name() { reentrantlock.lock(); try { //Do some work } catch(Exception e) { e.printStackTrace(); } finally { reentrantlock.unlock(); } }
ध्यान दें कि ReentrantLock का अनलॉक स्टेटमेंट हमेशा अंत में ब्लॉक होता है। यह गारंटी देता है कि एक अपवाद फेंक दिए जाने पर भी लॉक जारी किया जाता है।
ReentrantLock को समझने के लिए एक जावा प्रोग्राम लागू करें।
import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.*; import java.util.concurrent.locks.ReentrantLock; //thread class that implements Runnable interface class ThreadClass implements Runnable { String task_name; //define ReentrantLock object ReentrantLock thrd_lck; //ThreadClass constructor initialized lock and task name public ThreadClass(ReentrantLock r_lock, String t_name) { thrd_lck = r_lock; task_name = t_name; } //thread run () method public void run() { boolean bool_val = false; while (!bool_val) { //check for Outer Lock boolean tryLock_val = thrd_lck.tryLock(); // if lock is free, do the following if(tryLock_val) { try { for(int i=0;i<=6;i++) { if(i>=2) { thrd_lck.lock(); Thread thread_one = new Thread(); System.out.println('Thread Created.....'); if(i==3) { thread_one.setName('Maint Thread2'); System.out.println('Thread Created.....'); } } if(i==4) thrd_lck.unlock(); break; } System.out.println('ReentrantLock=>Is locked after sleep(1500) : ' + thrd_lck.isLocked()); System.out.println('Work done for task : ' + task_name ); bool_val = true; } catch(Exception e) { e.printStackTrace(); } } } } } public class Main { public static void main(String() args) { //define ReentrantLock lock object and service pool ReentrantLock reentrant_lock = new ReentrantLock(); ExecutorService pool = Executors.newFixedThreadPool(2); //create thread instance and pass lock and task name Runnable worker_thread = new ThreadClass(reentrant_lock, 'ThreadJob'); //execute the thread in exec pool pool.execute(worker_thread); //shut down the pool pool.shutdown(); } }
उत्पादन
उपरोक्त कार्यक्रम में, हमने एक धागा बनाया है और इसके लिए रेन्ट्रेंटलॉक का उपयोग किया है। ReentrantLock का उपयोग करके साझा संसाधन तक पहुँचा जा सकता है।
जावा में सेमाफोर
थ्रेड सिंक्रोनाइज़ेशन की अगली विधि सेमाफोर का उपयोग करके है। इस निर्माण को सेमाफोर कहा जाता है, एक साझा संसाधन तक पहुंच को एक काउंटर के माध्यम से नियंत्रित किया जाता है। सिग्नल थ्रेड्स के बीच भेजे जाते हैं ताकि हम महत्वपूर्ण सेक्शन की रखवाली कर सकें और मिस्ड सिग्नल से भी बच सकें।
एक सेमाफोर को एक चर के रूप में परिभाषित किया जा सकता है जो इन प्रक्रियाओं को सिंक्रनाइज़ करके समवर्ती प्रक्रियाओं का प्रबंधन करने के लिए उपयोग किया जाता है। साझा संसाधनों तक पहुंच को सिंक्रनाइज़ करने के लिए सेमाफोरस का उपयोग किया जाता है और इस तरह एक दौड़ की स्थिति से बचा जाता है। सेमाफोर द्वारा साझा संसाधन तक पहुंचने के लिए एक धागे को दी गई अनुमति को परमिट भी कहा जाता है।
वे कौन से कार्य करते हैं, इसके आधार पर सेमाफोरस को दो प्रकारों में विभाजित किया जा सकता है:
(1) बाइनरी सेमाफोर: एक बाइनरी सेमाफोर का उपयोग समवर्ती प्रक्रियाओं को सिंक्रनाइज़ करने और पारस्परिक बहिष्करण को लागू करने के लिए किया जाता है। एक बाइनरी सेमाफोर केवल दो मानों अर्थात् 0 और 1 को मानता है।
# 2) गणना सेमाफोर: काउंटिंग सेमाफोर में एक मूल्य होता है जो महत्वपूर्ण अनुभाग में प्रवेश करने वाली प्रक्रियाओं की संख्या को इंगित करता है। किसी भी बिंदु पर, मूल्य महत्वपूर्ण अनुभाग में प्रवेश करने वाली अधिकतम प्रक्रियाओं को इंगित करता है।
तो एक सेमाफोर कैसे काम करता है?
एक सेमाफोर के काम को निम्नलिखित चरणों में संक्षेपित किया जा सकता है:
- यदि सेमाफोर गिनती> 0, इसका मतलब है कि धागे के पास महत्वपूर्ण अनुभाग तक पहुंचने की अनुमति है, और फिर गणना को घटाया जाता है।
- अन्यथा, परमिट प्राप्त होने तक धागा अवरुद्ध है।
- जब साझा किए गए संसाधन तक पहुँचने के साथ थ्रेड किया जाता है, तो परमिट जारी किया जाता है और अर्ध-गणना गणना बढ़ाई जाती है ताकि एक और धागा उपरोक्त चरणों को दोहरा सके और परमिट प्राप्त कर सके।
सेमाफोरस के कार्य के उपरोक्त चरणों को नीचे दिए गए फ़्लोचार्ट में संक्षेपित किया जा सकता है।
जावा में, हमें अपने सेमाफोर को लागू करने की आवश्यकता नहीं है लेकिन यह एक प्रदान करता है सिकंदरा वर्ग जो अर्ध-कार्यक्षमता को लागू करता है। सेमाफोर वर्ग का एक हिस्सा है java.util.concurrent पैकेज।
सेमाफोर वर्ग निम्नलिखित कंस्ट्रक्टर प्रदान करता है जिसके उपयोग से हम सेमीफोर ऑब्जेक्ट बना सकते हैं:
Semaphore (int num_value) Semaphore (int num_value, boolean how)
यहाँ,
num_value => परमिट गणना का प्रारंभिक मूल्य जो थ्रेड्स की संख्या निर्धारित करता है जो साझा संसाधन तक पहुंच सकते हैं।
कैसे => उस क्रम को सेट करता है जिसमें थ्रेड्स को परमिट (कैसे = सही) दिया जाएगा। यदि कैसे = गलत है, तो इस तरह के आदेश का पालन नहीं किया जाता है।
अब हम एक जावा प्रोग्राम को लागू करेंगे जो सेमीफ़ोर को प्रदर्शित करेगा जो कि साझा संसाधन एक्सेस को प्रबंधित करने और दौड़ की स्थिति को रोकने के लिए उपयोग किया जाता है।
import java.util.concurrent.*; //class for shared resource class SharedRes { static int count = 0; } class ThreadClass extends Thread { Semaphore sem; String threadName; public ThreadClass(Semaphore sem, String threadName) { super(threadName); this.sem = sem; this.threadName = threadName; } @Override public void run() { // Thread T1 processing if(this.getName().equals('T1')) { System.out.println('Start: ' + threadName); try { System.out.println(threadName + ' :waiting for a permit.'); // acquire the permit sem.acquire(); System.out.println(threadName + ':Acquired permit'); // access shared resource for(int i=0; i <5; i++) { SharedRes.count++; System.out.println(threadName + ': ' + SharedRes.count); Thread.sleep(10); } } catch (InterruptedException exc) { System.out.println(exc); } // Release the permit. System.out.println(threadName + ':Released the permit'); sem.release(); } // Thread T2 processing else { System.out.println('Start: ' + threadName); try { System.out.println(threadName + ':waiting for a permit.'); // acquire the lock sem.acquire(); System.out.println(threadName + ':Acquired permit'); // process the shared resource for(int i=0; i < 5; i++) { SharedRes.count--; System.out.println(threadName + ': ' + SharedRes.count); Thread.sleep(10); } } catch (InterruptedException exc) { System.out.println(exc); } // Release the permit. System.out.println(threadName + ':Released the permit.'); sem.release(); } } } public class Main { public static void main(String args()) throws InterruptedException { //create Semaphore=> #permits = 1 Semaphore sem = new Semaphore(1); // Create thread instances T1 & T2 //T1=> Increments the count; T2=> Decrements the count ThreadClass thread1 = new ThreadClass(sem, 'T1'); ThreadClass thread2 = new ThreadClass(sem, 'T2'); // start T1 & T2 thread1.start(); thread2.start(); // Wait T1 & T2 thread1.join(); thread2.join(); System.out.println('count: ' + SharedRes.count); // display final count. } }
उत्पादन
इस कार्यक्रम ने साझा संसाधन के लिए एक वर्ग घोषित किया। यह एक थ्रेड क्लास भी घोषित करता है जिसमें हमारे पास एक सेमीफोर वेरिएबल होता है जिसे क्लास कंस्ट्रक्टर में इनिशियलाइज़ किया जाता है।
थ्रेड वर्ग की ओवरराइड रन () विधि में, थ्रेड इंस्टेंस का प्रसंस्करण किया जाता है जिसमें थ्रेड परमिट प्राप्त करता है, एक साझा संसाधन तक पहुंचता है, और फिर परमिट जारी करता है।
मुख्य विधि में, हमने दो थ्रेड उदाहरणों की घोषणा की। दोनों धागे तब शुरू होते हैं और फिर वे जुड़ने की विधि का उपयोग करते हुए प्रतीक्षा करते हैं। अंत में, गिनती प्रदर्शित की जाती है अर्थात् 0 यह दर्शाता है कि दोनों धागे साझा संसाधन के साथ समाप्त हो गए हैं।
कांटा और जावा में शामिल हों
कांटा / ज्वाइन फ्रेमवर्क पहली बार जावा 7 में पेश किया गया था। इस फ्रेमवर्क में ऐसे उपकरण होते हैं जो समानांतर प्रसंस्करण को गति दे सकते हैं। यह सिस्टम में सभी उपलब्ध प्रोसेसर कोर का उपयोग करता है और कार्य को पूरा करता है। कांटा / जुड़ने के ढांचे में फूट डालो और जीतो दृष्टिकोण का उपयोग किया जाता है।
फोर्क / ज्वाइन फ्रेमवर्क के पीछे मूल विचार यह है कि पहला फ्रेमवर्क 'फोर्क्स', यानी जब तक कि कार्य परमाणु नहीं हो जाते हैं, तब तक कार्य को छोटे-छोटे अलग-अलग उप-प्रकारों में तोड़ देता है ताकि उन्हें एसिंक्रोनस तरीके से निष्पादित किया जा सके।
ऐसा करने के बाद, कार्य 'ज्वाइन' कर दिए जाते हैं यानी सभी उप-कार्य पुनरावर्ती रूप से एक ही कार्य या रिटर्न वैल्यू में शामिल हो जाते हैं।
फोर्क / जॉइन फ्रेमवर्क में थ्रेड्स का एक पूल है जिसे 'फोर्कजॉइनपूल' के रूप में जाना जाता है। यह पूल 'ForkJoinWorkerThread' प्रकार के श्रमिक सूत्र का प्रबंधन करता है जिससे प्रभावी समानांतर प्रसंस्करण प्रदान होता है।
ForkJoinPool कार्यकर्ता थ्रेड का प्रबंधन करता है और थ्रेड पूल प्रदर्शन और राज्य के बारे में जानकारी प्राप्त करने में भी हमारी मदद करता है। ForkJoinPool 'ExecutorService' का एक कार्यान्वयन है जिसकी हमने ऊपर चर्चा की थी।
वर्कर थ्रेड्स के विपरीत, ForkJoinPool प्रत्येक उपमा के लिए एक अलग थ्रेड नहीं बनाता है। ForkJoinPool में प्रत्येक थ्रेड कार्यों को संग्रहीत करने के लिए अपने deque (डबल-एंडेड कतार) को बनाए रखता है।
यह डॉक थ्रेड के वर्कलोड संतुलन के रूप में कार्य करता है और इसे नीचे वर्णित 'कार्य-चोरी एल्गोरिथ्म' की मदद से करता है।
काम चोरी एल्गोरिथम
हम सरल शब्दों में कार्य-चोरी एल्गोरिथ्म को परिभाषित कर सकते हैं 'यदि कोई धागा मुफ्त है, तो व्यस्त थ्रेड्स से काम चोरी करें'।
एक श्रमिक सूत्र को हमेशा अपने काम से काम मिलेगा। जब deque में सभी कार्य समाप्त हो जाते हैं और deque खाली हो जाता है, तो श्रमिक धागा किसी अन्य deque की पूंछ या 'वैश्विक प्रविष्टि कतार' से एक कार्य लेगा।
इस तरह से कार्यों के लिए थ्रेड्स के प्रतिस्पर्धा की संभावना कम से कम हो जाती है और काम के लिए थ्रेड को स्काउट करने की संख्या भी कम हो जाती है। ऐसा इसलिए है क्योंकि थ्रेड को पहले से ही उपलब्ध काम का सबसे बड़ा हिस्सा मिल गया है और इसे समाप्त कर दिया है।
तो हम एक कार्यक्रम में ForkJoinPool का उपयोग कैसे कर सकते हैं?
ForkJoinPool की सामान्य परिभाषा इस प्रकार है:
public class ForkJoinPool extends AbstractExecutorService
वर्ग ForkJoinPool 'java.util.concurrent' पैकेज का एक हिस्सा है।
जावा 8 में, हम अपनी स्थिर विधि 'कॉमन-पूल ()' का उपयोग करते हुए ForkJoinPool का एक उदाहरण बनाते हैं जो कॉमन पूल या डिफॉल्ट थ्रेड पूल का संदर्भ प्रदान करता है।
ForkJoinPool commonPool = ForkJoinPool.commonPool ();
जावा 7 में, हम एक ForkJoinPool उदाहरण बनाते हैं और इसे नीचे दिखाए गए अनुसार उपयोगिता वर्ग के क्षेत्र में असाइन करते हैं।
public static ForkJoinPool forkJoinPool = new ForkJoinPool(2);
उपरोक्त परिभाषा इंगित करती है कि पूल में 2 का समानांतर स्तर है जैसे कि पूल 2 प्रोसेसर कोर का उपयोग करेगा।
उपरोक्त पूल तक पहुंचने के लिए, हम निम्नलिखित बयान दे सकते हैं।
ForkJoinPool forkJoinPool = PoolUtil.forkJoinPool;
ForkJoinPool कार्यों के लिए आधार प्रकार 'ForkJoinTask' है। हमें इसके एक उपवर्ग का विस्तार करना चाहिए अर्थात् शून्य कार्यों के लिए, रिकर्सिवएशन और एक मान लौटाने वाले कार्यों के लिए, रिकर्सिवटैस्क। दोनों विस्तारित वर्ग एक अमूर्त विधि गणना () प्रदान करते हैं जिसमें हम कार्य के तर्क को परिभाषित करते हैं।
नीचे दिए गए ForkJoinPool को प्रदर्शित करने के लिए एक उदाहरण है।
import java.util.ArrayList; import java.util.List; import java.util.concurrent.*; //class declaration for ForkJoinPool tasks class FJPoolTask extends RecursiveAction { private long Load = 0; public FJPoolTask(long Load) { this.Load = Load; } @Override protected void compute() { //if threshold is reached, break tasks into smaller tasks List subtasks = new ArrayList(); subtasks.addAll(createSubtasks()); for(RecursiveAction subtask : subtasks){ subtask.fork(); } } //create subtasks private List createSubtasks() { List sub_tasks =new ArrayList(); FJPoolTask sub_task1 = new FJPoolTask(this.Load / 2); FJPoolTask sub_task2 = new FJPoolTask(this.Load / 2); FJPoolTask sub_task3 = new FJPoolTask(this.Load / 2); sub_tasks.add(sub_task1); sub_tasks.add(sub_task2); sub_tasks.add(sub_task3); return sub_tasks; } } public class Main { public static void main(final String() arguments) throws InterruptedException { //get count of available processors int proc = Runtime.getRuntime().availableProcessors(); System.out.println('Processors available:' +proc); //declare forkJoinPool ForkJoinPool Pool = ForkJoinPool.commonPool(); System.out.println(' Active Threads (Before invoke):' +Pool.getActiveThreadCount()); //Declare ForkJoinPool task object FJPoolTask t = new FJPoolTask(400); //submit the tasks to the pool Pool.invoke(t); System.out.println(' Active Threads (after invoke):' +Pool.getActiveThreadCount()); System.out.println('Common Pool Size :' +Pool.getPoolSize()); } }
उत्पादन
उपरोक्त कार्यक्रम में, हम 'इनवोक ()' विधि को कॉल करने से पहले और बाद में सिस्टम में सक्रिय थ्रेड्स की संख्या पाते हैं। पूल में कार्यों को जमा करने के लिए इनवोक () विधि का उपयोग किया जाता है। हम सिस्टम में उपलब्ध प्रोसेसर कोर की संख्या भी पाते हैं।
बार बार पूछे जाने वाले प्रश्न
Q # 1) Java Util Concurrent क्या है?
उत्तर: पैकेज 'java.util.concurrent' समवर्ती (बहु-थ्रेडेड) अनुप्रयोगों के विकास को सुविधाजनक बनाने के लिए जावा द्वारा प्रदान की गई कक्षाओं और इंटरफेस का एक सेट है। इस पैकेज का उपयोग करके हम अपनी कक्षाओं को लिखने के बिना इंटरफ़ेस और कक्षाओं के साथ-साथ एपीआई का भी सीधे उपयोग कर सकते हैं।
Q # 2) निम्नलिखित में से कौन से समवर्ती कार्यान्वयन java.util में मौजूद हैं। समवर्ती पैकेज?
उत्तर: एक उच्च स्तर पर, java.util.concurrent पैकेज में एक्जिक्यूटर्स, सिंक्रोनाइज़र, क्यू, टाइमिंग और समवर्ती संग्रह जैसी उपयोगिताओं हैं।
Q # 3) भविष्य जावा क्या है?
उत्तर: भविष्य की वस्तु (java.util.concurrent.Future) का उपयोग थ्रेड द्वारा लौटाए गए परिणाम को स्टोर करने के लिए किया जाता है जब कॉल करने योग्य इंटरफ़ेस लागू किया जाता है।
विंडोज़ 10 64 बिट के लिए मोंगोडब डाउनलोड करें
Q # 4) जावा में थ्रेड-सेफ क्या है?
उत्तर: जावा में एक थ्रेड-सुरक्षित कोड या वर्ग एक कोड या वर्ग है जिसे किसी भी समस्या के बिना बहु-थ्रेडेड या समवर्ती वातावरण में साझा किया जा सकता है और अपेक्षित परिणाम पैदा कर सकता है।
Q # 5) जावा में सिंक्रनाइज़ संग्रह क्या है?
उत्तर: एक सिंक्रनाइज़ संग्रह एक थ्रेड-सुरक्षित संग्रह है। Java.util.Collections वर्ग की विधि सिंक्रनाइज़ संग्रह () सिंक्रनाइज़ (थ्रेड-सुरक्षित) संग्रह लौटाता है।
निष्कर्ष
इस ट्यूटोरियल के साथ, हमने जावा में मल्टी-थ्रेडिंग और कॉन्सिक्वेरिटी का विषय पूरा कर लिया है। हमने अपने पिछले ट्यूटोरियल में विस्तार से चर्चा की है। यहां, हमने संगामिति और संगति से संबंधित कार्यान्वयन और मल्टीथ्रेडिंग पर चर्चा की जो java.util.concurrent पैकेज का एक हिस्सा है।
हमने दो और सिंक्रोनाइज़ेशन विधियों, सेमाफोरस और रेअन्ट्रेंटलॉक पर चर्चा की। हमने ForkJoinPool पर भी चर्चा की जिसका उपयोग कार्यों को सरल कार्यों में विभाजित करके और फिर अंततः परिणाम में शामिल करने के लिए किया जाता है।
Java.util.concurrent पैकेज एक्ज़ीक्यूटर फ्रेमवर्क और एक्ज़ीक्यूटर्स का भी समर्थन करता है जो हमें थ्रेड्स निष्पादित करने में मदद करते हैं। हमने थ्रेड पूल कार्यान्वयन पर भी चर्चा की जिसमें पुन: प्रयोज्य थ्रेड होते हैं जो निष्पादन समाप्त होने पर पूल में वापस आ जाते हैं।
हमने Runnable के समान एक और इंटरफ़ेस पर चर्चा की जो हमें थ्रेड से परिणाम वापस लाने में मदद करता है और फ्यूचर ऑब्जेक्ट प्राप्त थ्रेड रिजल्ट को स्टोर करने के लिए उपयोग किया जाता है।
=> यहाँ सरल जावा प्रशिक्षण श्रृंखला देखें।
अनुशंसित पाठ
- थ्रेड स्लीप () - थ्रेड स्लीप () विधि जावा में उदाहरण के साथ
- जावा परिनियोजन: जावा जार फ़ाइल का निर्माण और निष्पादन
- जावा बेसिक्स: जावा सिंटैक्स, जावा क्लास और कोर जावा कॉन्सेप्ट
- जावा वर्चुअल मशीन: जावा एप्लीकेशन चलाने में JVM कैसे मदद करता है
- जावा में एक्सेस संशोधक - उदाहरण के साथ ट्यूटोरियल
- जावा सिंक्रोनाइज़्ड: जावा में थ्रेड सिंक्रोनाइज़ेशन क्या है
- जावा ट्यूटोरियल फॉर बिगिनर्स: 100+ हैंड्स-ऑन जावा वीडियो ट्यूटोरियल
- उदाहरण के लिए जावा इंटेगर और जावा बिगइंटर क्लास