2010-06-02 6 views
6

मान लीजिए कि हमारे पास खाता सेवा नामक एक वर्ग है जो खातों की स्थिति का प्रबंधन करता है।एक विधि लिखने के सर्वोत्तम तरीके जो मल्टीथ्रेड किए गए जावा वातावरण में दो ऑब्जेक्ट्स अपडेट करते हैं?

AccountService

interface AccountService{ 
public void debit(account); 
public void credit(account); 
public void transfer(Account account, Account account1); 

} 

इस परिभाषा को देखते हुए के रूप में परिभाषित किया गया है, हस्तांतरण को लागू करने() ताकि आप सुनिश्चित कर सकते हैं कि स्थानांतरण एक परमाणु ऑपरेशन है सबसे अच्छा तरीका क्या है।

मैं जवाब का संदर्भ है कि जावा 1.4 कोड के साथ ही उत्तर दोनों Account वस्तुओं पर जावा 5

उत्तर

11

सिंक्रनाइज़ में java.util.concurrent से संसाधनों का उपयोग और हस्तांतरण कर सकता है कि में दिलचस्पी रखता हूँ। सुनिश्चित करें कि आप हमेशा एक ही क्रम में सिंक्रनाइज़ करते हैं। ऐसा करने के लिए, Account एस Comparable लागू करें, दो खातों को सॉर्ट करें, और उस क्रम में सिंक्रनाइज़ करें।

आप खातों का आदेश नहीं है, तो आप गतिरोध की संभावना अगर एक धागा ए

करने के लिए बी करने के लिए एक से स्थानान्तरण और बी से दूसरे स्थानान्तरण यह सटीक उदाहरण Java Concurrency in Practice के 207 पृष्ठ पर चर्चा की है चलाने के लिए, बहु-थ्रेडेड जावा विकास करने वाले किसी के लिए एक महत्वपूर्ण पुस्तक। उदाहरण के कोड publisher's website से उपलब्ध है:

+0

गतिरोध – unbeli

+1

+1 से बचने के लिए +1: हमेशा उसी क्रम में वस्तुओं पर ताला लगा overstressed नहीं जा सकता है। – Powerlord

+0

यदि प्रदर्शन महत्वपूर्ण नहीं है, और एक खाता खाता है, तो कोई भी स्थानांतरण-विधि को 'सिंक्रनाइज़' कर सकता है। – aioobe

2

आप शायद एक पूर्ण लेनदेन समर्थन का (अगर यह निश्चित रूप से एक असली आवेदन है) की जरूरत है।

समाधान की कठिनाई शायद आपके पर्यावरण पर निर्भर करती है। अपने सिस्टम को विस्तार से बताएं और हम आपकी मदद करने की कोशिश करेंगे (किस प्रकार का एप्लिकेशन? यह वेब-सर्वर का उपयोग करता है? कौन सा वेब-सर्वर? डेटा स्टोर करने के लिए क्या उपयोग किया जाता है? और इसी तरह)

+0

+1। –

0

आप खाते की शेष राशि के लिए एक AtomicReference<Double> का उपयोग कर, get() और set() के साथ सिंक्रनाइज़ करने के लिए होने से बचने नहीं कर सकता है?

+1

तुम अब भी अधिक सिंक्रनाइज़ करने के लिए होगा आपरेशन के रूप में, कि 'AtomicReference ' को पहुँचता 'bal.set (bal.get() + creditAmt)' परमाणु नहीं है: कुछ कॉल के बीच में रेफरी बाल द्वारा आयोजित बदल सकता है 'प्राप्त करें()' और 'सेट() '। लेकिन यह एक परमाणु संचालन में दो संचालन (डेबिट और क्रेडिट) को बांधने की बात है, खासकर यदि कोई विफल रहता है, तो दूसरा भी विफल हो जाएगा (रोलबैक)। –

+0

सुनिश्चित करना कि मैं सही ढंग से समझ ... समन्वयन के बिना परमाणु वेरिएबल का उपयोग करके संभावित समस्या क्या मेरी पत्नी (मेरी कॉल के बीच बाल का मूल्य बदल रहा प्राप्त करने के लिए() और सेट()) करता है, बल्कि, क्या मेरी पूर्व पत्नी नहीं है करता है (प्राप्त करने के लिए मेरी कॉल के बीच बाल के संदर्भ को बदलना() और सेट())? के बाद से आप एक संदर्भ ('AtomicReference - bal') है – Pete

+0

" मूल्य "और" संदर्भ "इस संदर्भ में उपयोग करने के लिए मुश्किल हो जाता है के लिए एक संदर्भ के लिए (' डबल - bal.value') एक डबल मूल्य ('डबल करने के लिए - bal.value .value')। संदर्भ 'बाला' अंतिम होगा, इसलिए यह कोई मुद्दा नहीं है। सिंक्रनाइज़ेशन समस्या गणित करने में आती है, जहां आपको पहले 1 की आवश्यकता होती है) 'परमाणु संदर्भ से डबल प्राप्त करें), 2) उस पर गणित करें, और 3)' मान() 'मान वापस करें। यदि आप 1-2-3 परमाणु नहीं बनाते हैं, तो आपके पास थ्रेड ए डू 1 और 2 हो सकता है, फिर थ्रेड बी डू 1, फिर थ्रेड ए डू 3, फिर थ्रेड बी डू 2 और 3 ए के परिवर्तनों को मारना। –

1

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

है, तो आप उन सब को एक भी वैश्विक लॉक का उपयोग करने का फैसला हो सकता है। ऐसा करने का एक आसान तरीका उन सभी कोड को घेरे रखना है जो सिंक्रनाइज़ (X) {...} ब्लॉक में खाता सेवा का उपयोग करते हैं, जहां एक्स कुछ साझा/सिंगलटन ऑब्जेक्ट उदाहरण है (जो खाता सेवा उदाहरण स्वयं हो सकता है)। यह थ्रेड सुरक्षित होगा क्योंकि केवल एक ही थ्रेड किसी भी समय खाता सेवा का उपयोग करेगा, भले ही वे विभिन्न तरीकों से हों।

हैं कि अभी भी पर्याप्त नहीं है, तो आप और अधिक परिष्कृत लॉकिंग दृष्टिकोण का उपयोग करना होगा। एक आम तरीका उनको संशोधित करने से पहले अलग-अलग खातों को लॉक करना होगा ... लेकिन फिर आपको ताले को लगातार क्रम में ले जाने के लिए बहुत सावधान रहना चाहिए (उदाहरण के लिए खाता आईडी द्वारा) अन्यथा आप डेडलॉक्स में भाग लेंगे।

अंत में अगर AccountService एक दूरस्थ सेवा है, तो आप वितरित ताला क्षेत्र में कर रहे हैं .... जब तक आप कंप्यूटर विज्ञान और अनुसंधान बजट के वर्षों में एक पीएचडी आप को जलाने के लिए शायद वहाँ जाने से बचना चाहिए है।

+0

पीएस सादगी और सामान्यता के लिए मैं वैश्विक लॉक के लिए जाऊंगा जब तक कि आप एक प्रदर्शन/स्केलेबिलिटी समस्या (जो कभी नहीं हो!) मारा जाए और केवल तब ही खाता-स्तर लॉकिंग पर जाने के बारे में सोचना शुरू कर दें। – mikera

+0

मैं आपके उत्तर की पूर्णता की सराहना करता हूं। आपने अपनी टिप्पणी में जो उल्लेख किया है वह वही है जहां मैं नेतृत्व कर रहा था। एक बार आपको खाता स्तर लॉकिंग प्राप्त करने के बाद ऐसा लगता है कि 'सिंक्रनाइज़' कीवर्ड का उपयोग करने की तुलना में अधिक विस्तृत योजना की आवश्यकता है। –

+0

@DanielHonig: यह सच है कि समयपूर्व अनुकूलन से बचने के लिए अच्छा है, लेकिन कई या अधिकांश लेनदेन प्रसंस्करण प्रणालियों में (जहां मशीन का काम मूल रूप से लाखों अभिलेखों पर पूरे दिन ऐसा करने के लिए होता है) डिजाइनर कभी भी उपयोग करने का प्रयास नहीं करेगा एक वैश्विक म्यूटेक्स। उन्हें अक्सर कई (कई!) कोर के साथ डिजाइन किया जाता है और जितना संभव हो सके समानांतर में काम करने की कोशिश करता है। तो यह जानना अच्छा है कि इस डोमेन में खाता-आधारित लॉकिंग क्यों उपयोगी है। –

संबंधित मुद्दे

 संबंधित मुद्दे