2010-06-23 14 views
10

एक अच्छा article with some concurrency tips में, एक उदाहरण निम्नलिखित लाइनों के लिए अनुकूलित किया गया था:अंतर पढ़ता है और अस्थिर

double getBalance() { 
    Account acct = verify(name, password); 
    synchronized(acct) { return acct.balance; } 
} 

तो मैं समझता हूँ कि सही ढंग से, तुल्यकालन की बात सुनिश्चित करने के लिए कि का मूल्य है acct.balance कि इस सूत्र द्वारा पढ़ा जाता है वर्तमान है और वह कोई भी लंबित acct.balance में वस्तु के क्षेत्र के लिए लिखते हैं भी मुख्य स्मृति में लिखा जाता है।

उदाहरण ने मुझे थोड़ा सोचा: क्या यह volatile के रूप में acct.balance (यानी वर्ग खाते का क्षेत्र संतुलन) घोषित करने के लिए अधिक कुशल नहीं होगा? यह और अधिक कुशल होना चाहिए आप पहुंच acct.balance करने पर सभी synchronize बचाने के लिए और पूरे acct वस्तु ताला नहीं होता। क्या मैं कुछ भूल रहा हूँ?

+0

आप सही हैं, लेकिन लेख कुछ पूरी तरह से अलग के बारे में वास्तव में है - ताला गुंजाइश को कम करने। – gustafc

उत्तर

13

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

आप खाते की राशि का समायोजन करने का एक तरीका है कि ग्रहण करने के लिए थे, तो अस्थिर नहीं काफी अच्छा

public void add(double amount) 
{ 
    balance = balance + amount; 
} 

फिर हम अगर संतुलन कोई अन्य तुल्यकालन के साथ अस्थिर है एक समस्या है है। दो धागे कोशिश करते हैं और (जोड़ने कॉल करने के लिए थे, तो) एक साथ आप एक "याद" अद्यतन जहां निम्नलिखित

Thread1 - Calls add(100) 
Thread2 - Calls add(200) 
Thread1 - Read balance (0) 
Thread2 - Read balance (0) 
Thread1 - Compute new balance (0+100=100) 
Thread2 - Compute new balance (0+200=200) 
Thread1 - Write balance = 100 
Thread2 - Write balance = 200 (WRONG!) 

होता है जाहिर है यह गलत है क्योंकि दोनों धागे वर्तमान मूल्य पढ़ सकते हैं और स्वतंत्र रूप से अद्यतन और फिर इसे वापस लिखा हो सकता था (पढ़ें, गणना करें, लिखें)। अस्थिरता यहां मदद नहीं करती है, इसलिए आपको यह सुनिश्चित करने के लिए सिंक्रनाइज़ करने की आवश्यकता होगी कि एक धागा अन्य धागे शुरू होने से पहले पूरे अपडेट को पूरा कर ले।

मैं सामान्य पाते हैं जब कुछ कोड लिखने मुझे लगता है कि "मैं बजाय सिंक्रनाइज़ के अस्थिर उपयोग कर सकते हैं" जवाब अच्छी तरह से "हाँ" लेकिन गलत यह सुनिश्चित करने के लिए यह पता लगाना के समय/प्रयास और होने का खतरा हो सकता है लाभ के लायक नहीं है (मामूली प्रदर्शन)।

एक के रूप में एक तरफ एक अच्छी तरह से लिखा खाता वर्ग आंतरिक रूप से सभी सिंक तर्क संभाल तो कॉल इसके बारे में चिंता करने की ज़रूरत नहीं होगी।

1

खाते के रूप में अस्थिर मुद्दों और प्रतिबंध

1. निम्नलिखित के अधीन है की घोषणा "जब से अन्य थ्रेड स्थानीय चर, स्थानीय चर घोषित करने अस्थिर व्यर्थ है नहीं देख सकता।" इसके अलावा यदि आप किसी विधि में अस्थिर चर घोषित करने का प्रयास करते हैं, तो आपको कुछ मामलों में एक कंपाइलर त्रुटि मिल जाएगी।

डबल getBalance() { अस्थिर खाता खाते पर = सत्यापित करें (नाम, पासवर्ड); // गलत .. }

  1. खाते के रूप में अस्थिर घोषणा ने चेतावनी दी है को संकलक उन्हें ताजा लाने के लिए हर बार, बजाय उन्हें रजिस्टरों में कैशिंग से। यह भी कुछ ऑप्टिमाइज़ेशन को रोकता है जो मानते हैं कि कोई अन्य धागा अप्रत्याशित रूप से मानों को बदल देगा।

  2. आप समन्वय करने के लिए विभिन्न धागे से चर में परिवर्तन, सिंक्रनाइज़ की जरूरत है अस्थिर आप परमाणु पहुँच की गारंटी नहीं है, क्योंकि एक अस्थिर चर तक पहुँचने एक ताला कभी नहीं रखती है, यह ऐसे मामलों में जहां हम चाहते हैं के लिए उपयुक्त नहीं है एक परमाणु ऑपरेशन के रूप में पढ़ने-अद्यतन-लिखने के लिए। जब तक आप सुनिश्चित न हों कि acct = सत्यापित करें (नाम, पासवर्ड); एकल परमाणु ऑपरेशन है, आप

  3. यदि वेरिएबल एक्ट एक ऑब्जेक्ट संदर्भ है, तो संभावना है कि यह शून्य हो सकता है। एक शून्य ऑब्जेक्ट पर सिंक्रनाइज़ करने का प्रयास सिंक्रनाइज़ किए गए एक NullPointerException फेंक देगा। (क्योंकि आप प्रभावी रूप से संदर्भ पर सिंक्रनाइज़ कर रहे हैं, न कि वास्तविक वस्तु) के रूप में अस्थिर कहाँ

  4. शिकायत नहीं है इसके बजाय आप यहाँ की तरह अस्थिर रूप में एक बूलियन चर घोषित कर सकता है

    निजी अस्थिर बूलियन someAccountflag;

    सार्वजनिक शून्य प्राप्त संतुलन() { खाता एक्ट; जबकि (! SomeAccountflag) { acct = सत्यापित करें (नाम, पासवर्ड); } }

नोट आप सिंक्रनाइज़ के रूप में someAccountflag की घोषणा नहीं कर सकते हैं, के रूप में आप एक आदिम सिंक्रनाइज़ साथ पर सिंक्रनाइज़ नहीं कर सकते हैं, सिंक्रनाइज़ केवल वस्तु चर, जहां आदिम या वस्तु के रूप में चर अस्थिर घोषित किया जा सकता है के साथ काम करता

6. कक्षा अंतिम स्थैतिक क्षेत्रों को अस्थिर होने की आवश्यकता नहीं है, JVM इस समस्या का ख्याल रखता है। तो कुछ अकाउंटफ्लैग को अस्थिर घोषित करने की आवश्यकता नहीं है यदि यह अंतिम स्थिर है या आप आलसी ऑब्जेक्ट के रूप में खाता बनाने वाले आलसी सिंगलटन प्रारंभिक उपयोग का उपयोग कर सकते हैं और इसे निम्नानुसार घोषित कर सकते हैं: निजी अंतिम स्थैतिक खाता सिंगलेटन acc_singleton = new AccountSingleton();

+0

उत्तर के लिए धन्यवाद, लेकिन मैंने वास्तव में सुझाव दिया कि एसीटी को अस्थिर घोषित न करें, लेकिन acct.balance - यानी, वर्ग खाते का क्षेत्र संतुलन। यह आपकी कुछ टिप्पणियों को संशोधित करता है। मैंने इस संबंध में थोड़ा सा सवाल स्पष्ट करने की कोशिश की। –

1

यदि एकाधिक थ्रेड डेटा को संशोधित और एक्सेस कर रहे हैं, तो synchronized एकाधिक धागे के बीच डेटा स्थिरता की गारंटी देता है।

यदि सिंगल थ्रेड डेटा को संशोधित कर रहा है और एकाधिक थ्रेड डेटा के नवीनतम मूल्य को पढ़ने का प्रयास करते हैं, तो volatile निर्माण का उपयोग करें।

लेकिन उपरोक्त कोड के लिए, volatile कई थ्रेड संशोधित संतुलन को स्मृति स्थिरता की गारंटी नहीं देता है। AtomicReferenceDouble प्रकार के साथ आपके उद्देश्य की सेवा करता है।

संबंधित एसई प्रश्न:

Difference between volatile and synchronized in Java