जावा में उत्कृष्ट संगामिति और लॉकिंग समर्थन है - यकीनन आधुनिक भाषाओं में सबसे अच्छा। इस तथ्य के अलावा कि भाषा में ही अंतर्निहित सिंक्रनाइज़ेशन समर्थन है, AQS ढांचे के आधार पर कई उपयोगी उपयोगिताएं हैं। इनमें काउंटडाउनचैच, बैरियर, सेमाफोर और अन्य शामिल हैं। हालांकि, ऐसी स्थिति का अक्सर सामना किया जाता है जो सीधे समर्थित नहीं होती है: जब किसी
विशिष्ट वस्तु तक पहुंच को अवरुद्ध करना आवश्यक नहीं है, लेकिन
उस वस्तु के
विचार के लिए ।
निम्नलिखित उदाहरण पर विचार करें: हमारे पास एक सेवा है जो मेलबॉक्सों के लिए जिम्मेदार है। इसमें एक नया संदेश जोड़ने के लिए एक विधि है और संदेश पढ़ने के लिए एक विधि है। बेशक, हम अपनी सेवा को बहु-थ्रेडेड वातावरण में लॉन्च करते हैं - क्योंकि हमारे पास हजारों उपयोगकर्ता हैं जो एक दूसरे को संदेशों का असंख्य संदेश भेजते हैं। मेलबॉक्स को डेटा भ्रष्टाचार से बचाने के लिए, हमें एक ही समय में कई थ्रेड्स द्वारा एक मेलबॉक्स के संशोधन की अनुमति नहीं देनी चाहिए:
public void sendMessage(UserId from, UserId to, Message message) { Mailbox sendersBox = getMailbox(from); Mailbox recipientsBox = getMailbox(to); min(sendersBox,recipientsBox).lock(); max(sendersBox, recipientsBox).lock(); sendersBox.addIncomingMessage(message); recipientsBox.addSentMessage(message); max(sendersBox, recipientsBox).lock(); min(sendersBox,recipientsBox).lock(); }
कोड मानता है कि मेलबॉक्स के लिए हमारे पास एक न्यूनतम / अधिकतम फ़ंक्शन है जो हमेशा एक अस्पष्ट परिणाम देता है: उदाहरण के लिए, यह हैश कोड, या होस्ट आईडी, या कुछ और की तुलना करता है। यह आवश्यक है ताकि दो मेलबॉक्सेस को संशोधित करते समय लॉक को हमेशा उसी क्रम में रखा जाए (पहले छोटा) और गतिरोध को रोका जा सके। इसे ध्यान में रखते हुए, कोड अपेक्षाकृत सुरक्षित दिखता है, है ना?
वास्तव में, इस कोड की सुरक्षा इस बात पर निर्भर करती है कि
getMailbox () विधि
वास्तव में कैसे लागू की जाती है। यदि वह एक ही वस्तु की वापसी की गारंटी दे सकता है (इसके अलावा, "एक ही", और "एक ही") नहीं, तो हम सुरक्षित हैं। दुर्भाग्य से, ऐसी गारंटी को लागू करना लगभग असंभव है। दूसरी ओर, हम निश्चित रूप से पूरे
SendMessage () पद्धति पर
सिंक्रनाइज़ कर सकते हैं, लेकिन यह कली में हमारी उत्पादकता को मार देगा, क्योंकि हम अपने निपटान में थ्रेड्स और प्रोसेसर की संख्या की परवाह किए बिना एक समय में केवल एक संदेश भेज सकते हैं।
यहाँ एक उदाहरण दिया गया है कि कैसे
getMailbox () को गलत तरीके से लागू किया जा सकता है:
private Mailbox getMailbox(UserId id) { Mailbox mailbox = cache.get(id); if (mailbox == null) { mailbox = new Mailbox(); cache.put(id, mailbox); } return mailbox; }
जाहिर है, यह कार्यान्वयन असुरक्षित है: यह एक ही मेलबॉक्स को सार में (यानी एक ही उपयोगकर्ता से संबंधित) एक साथ अलग-अलग थ्रेड से बनाने की अनुमति देता है। इसका मतलब यह है कि सिस्टम में कुछ बिंदु पर एक उपयोगकर्ता के दो अलग-अलग बॉक्स दिखाई दे सकते हैं, और केवल Cthulhu को पता चलेगा कि कौन से जीवित रहेगा। आप विश्व स्तर पर एक नए मेलबॉक्स के निर्माण को अवरुद्ध करके इस समस्या को हल कर सकते हैं, लेकिन यह, फिर से, प्रदर्शन के साथ एक रेक है। आप
कॉन्ट्रैक्टरपार्ट के साथ मस्ती करना शुरू कर सकते हैं और वहां सभी तरह के
पुटफैबसेंट लगा सकते हैं। लेकिन कल्पना करें कि हम न केवल नए मेलबॉक्स बनाते हैं, बल्कि डेटाबेस से मौजूदा लोड भी करते हैं। तब सही ढंग से सिंक्रनाइज़ करना अधिक कठिन होगा (और डेटाबेस में अनावश्यक प्रश्नों को रोकना अच्छा होगा)।
सौभाग्य से, IdBasedLocking वास्तव में इस समस्या को हल करता है।
एक विशिष्ट "मेलबॉक्स"
ऑब्जेक्ट को
अवरुद्ध करने के बजाय, हम इस तथ्य के आधार पर
मेलबॉक्स की अवधारणा को अवरुद्ध करते हैं कि हमारे पास प्रति उपयोगकर्ता एक मेलबॉक्स है:
IdBasedLockManager<UserId> manager = new SafeIdBasedLockManager<UserId>(); public void sendMessageSafe(UserId from, UserId to, Message message) { IdBasedLock<UserId> minLock = manager.obtainLock(min(from, to)); IdBasedLock<UserId> maxLock = manager.obtainLock(max(from, to)); minLock.lock(); maxLock.lock(); try { Mailbox sendersBox = getMailbox(from); Mailbox recipientsBox = getMailbox(to); sendersBox.addIncomingMessage(message); recipientsBox.addSentMessage(message); } finally { maxLock.unlock(); minLock.unlock(); } }
एक उदाहरण दो वस्तुओं के एक साथ संशोधन के कारण कृत्रिम रूप से जटिल लग सकता है। IdBasedLocking एक एकल ऑब्जेक्ट के साथ ठीक काम करता है, जैसा कि निम्नलिखित काउंटर उदाहरण दर्शाता है।
सबसे पहले, एक टूटा हुआ संस्करण:
public void increaseCounterUnsafe(String id) { Counter c = counterCache.get(id); if (c == null) { c = new Counter(); counterCache.put(id, c); } c.increase(); }
और अब सुरक्षित संस्करण:
public void increaseCounterSafely(String id) { IdBasedLock<String> lock = lockManager.obtainLock(id); lock.lock(); try{ Counter c = counterCache.get(id); if (c == null) { c = new Counter(); counterCache.put(id, c); } c.increase(); } finally { lock.unlock(); } }
गिडब पर IdBasedLocking परियोजना।
आप इसे मावेन, ग्रेडल या आइवी या केवल
केंद्रीय से उपयोग कर सकते हैं। या अपने आप को खुद एक
गितुब पर बनाएँ।
आपका ध्यान देने के लिए धन्यवाद।