2012-05-22 10 views
7

इस विषय पर कुछ प्रश्न हैं, लेकिन इस समस्या के आसपास सबसे अधिक स्कर्ट क्योंकि यह सवाल का इरादा नहीं है।क्या मुझे स्थिर अस्थिर चर को सिंक्रनाइज़ करना चाहिए?

अगर मैं मेरी कक्षा में एक स्थिर अस्थिर है:

private static volatile MyObj obj = null; 

और एक विधि में नीचे मैं करता हूँ:

public MyObj getMyObj() { 
    if (obj == null) { 
     obj = new MyObj();// costly initialisation 
    } 
    return obj; 
} 

मैं केवल एक धागा क्षेत्र के लिए लिखते हैं सुनिश्चित करने के लिए सिंक्रनाइज़ करना होगा , या obj == null सशर्त मूल्यांकन का मूल्यांकन करने वाले अन्य धागे के लिए तत्काल दिखाई देगा?

इसे एक और तरीका रखने के लिए: क्या अस्थिरता आपको स्थैतिक चर पर लिखने के लिए सिंक्रनाइज़ करने के आसपास मिलती है?

उत्तर

8

आपको निश्चित रूप से कुछ लॉकिंग की आवश्यकता होगी ताकि यह सुनिश्चित किया जा सके कि केवल एक धागा फ़ील्ड को लिखता है। अस्थिरता के बावजूद, दो धागे दोनों "देख सकते हैं" कि obj शून्य है, और फिर दोनों आपके वर्तमान कोड के साथ प्रारंभ करना शुरू करते हैं।

व्यक्तिगत तौर पर मैं काफ़ी होगा तीन विकल्पों में से एक:

  • प्रारंभ वर्ग लोड पर (जानते हुए भी कि कि आलसी होगा, लेकिन आलसी के रूप में नहीं के रूप में getMyObj जब तक इंतज़ार कर पहले कहा जाता है):

    private static final MyObj obj = new MyObj(); 
    
    बिना शर्त ताला
  • उपयोग:

    private static MyObj obj; 
    private static final Object objLock = new Object(); 
    
    public static MyObj getMyObj() { 
        synchronized(objLock) { 
         if (obj == null) { 
          obj = new MyObj(); 
         } 
         return obj; 
        } 
    } 
    
  • आलस्य के लिए एक नेस्टेड वर्ग का प्रयोग करें कि जिस तरह से:

    public static MyObj getMyObj() { 
        return MyObjHolder.obj; 
    } 
    
    private static class MyObjHolder { 
        static final MyObj obj = new MyObj(); 
    } 
    
+0

मैं धारक मार्ग नहीं जा सकता: यह मेरे बाहरी वर्ग पर सिंगलटन को मजबूर करता है जिसके लिए मैं विकासशील समग्र ढांचे में कहीं अधिक सोच और परिवर्तन की आवश्यकता है। जिस समस्या को हल करने की आवश्यकता थी वह स्थैतिक उदाहरण की परमाणु रचना थी। मैंने एक स्थैतिक विधि का उपयोग किया और परमाणु असाइनमेंट और समवर्ती सुरक्षा की अनुमति देने के लिए इसे स्थिर ('obj') घोषित किया। –

+0

@atc: नहीं, धारक मार्ग बाहरी वर्ग को सिंगलटन होने के लिए मजबूर नहीं करता है। यह वास्तव में स्पष्ट नहीं है कि आपका मतलब क्या है ... –

+0

स्पष्टता के लिए: यह 'स्थैतिक अस्थिर' चर मूल रूप से ऑब्जेक्ट फैक्ट्री में था जिसने इसे रनटाइम पर इंजेक्शन दिया ताकि यह उदाहरण के लिए जिम्मेदार हो सके। यह कहा गया स्टेक var ('MyObj' ऊपर) के कार्यान्वयन के विवरण के कारण है। धारक मुहावरे को संतुष्ट करने के लिए 'निजी' कन्स्ट्रक्टर का उपयोग करना मतलब था कि मैं ऑब्जेक्ट फैक्ट्री के लिए विरासत के फायदे नहीं रख सका। यहां धारक मुहावरे को कम करने के बजाय, मैंने सोचा कि मैं डबल-चेक-सिंक्रनाइज़ रूट पर जाऊंगा क्योंकि कोडबेज के साथ कम से कम घर्षण था और काम करने के लिए साबित हुआ था। –

3

हां, आपको बिल्कुल सिंक्रनाइज़ करना चाहिए (या Singleton Holder idiom जैसे बेहतर मुहावरे का उपयोग करना चाहिए)। अन्यथा आप अपने ऑब्जेक्ट को कई बार शुरू करने वाले कई धागे का जोखिम चलाते हैं (और उसके बाद बाद में विभिन्न उदाहरणों का उपयोग करते हैं)।

इस तरह की घटनाओं की एक अनुक्रम पर विचार करें:

  1. थ्रेड एक getMyObj() प्रवेश करती है और देखता है कि obj == null
  2. थ्रेड बी में प्रवेश करती है getMyObj() और देखता है कि obj == null
  3. थ्रेड एक निर्माण करती है एक new MyObj() - यह objA कॉल
  4. थ्रेड बी new MyObj() बनाता है - चलिए इसे objB
  5. पर कॉल करें
  6. थ्रेड एक प्रदान करती है objAobj
  7. को थ्रेड बी प्रदान करती है objBobj (जो इस बिंदु पर अब और null नहीं है, इसलिए थ्रेड एक द्वारा आवंटित objA के संदर्भ में,, ओवरराइट है)
  8. थ्रेड एक बाहर निकालता है getMyObj() और शुरू होता है objA
  9. थ्रेड बी का उपयोग करने के getMyObj() बाहर निकालता है और objB

इस परिदृश्य उपयोग करने के लिए शुरू होता है किसी भी धागे के साथ हो सकता है। ध्यान दें कि यद्यपि, सादगी के लिए, मैंने घटनाओं का सख्त क्रम माना, वास्तविक बहुप्रचारित पर्यावरण कार्यक्रमों में 1-2, 3-4 और/या 7-8 आंशिक रूप से या पूरी तरह ओवरलैप कर सकते हैं, बिना अंत को बदल दिए परिणाम।

धारक मुहावरा करने के लिए एक उदाहरण:

public class Something { 
    private Something() { 
    } 

    private static class LazyHolder { 
     public static final Something INSTANCE = new Something(); 
    } 

    public static Something getInstance() { 
     return LazyHolder.INSTANCE; 
    } 
} 

यह गारंटी है, सुरक्षित है के रूप में INSTANCEfinal है। जावा मेमोरी मॉडल गारंटी देता है कि final फ़ील्ड प्रारंभिक कक्षा को लोड करने पर, किसी भी संख्या में धागे को सही ढंग से दिखाई देकर दिखाई दे रहे हैं। चूंकि LazyHolderprivate है और केवल getInstance() द्वारा संदर्भित किया गया है, यह केवल तब लोड हो जाएगा जब getInstance() पहले कॉल किया जाता है। और उस बिंदु पर, INSTANCE पृष्ठभूमि में आरंभ किया गया है और JVM द्वारा सुरक्षित रूप से प्रकाशित किया गया है।

+0

मैं सिर्फ क्षेत्र स्थिर हो सकता हूं और सृजन करने के लिए एक स्थिर प्रारंभिक या स्थैतिक विधि का उपयोग कर सकता हूं, सिंक्रनाइज़ करने और तर्क को पकड़ने/पकड़ने की अनुमति देता हूं। इस तरह यह अधिक पठनीय होगा + मुझे 'getMyObj()' कॉल पर लॉक नहीं होना पड़ेगा क्योंकि क्लास उपलब्ध होने से पहले स्थिर प्रारंभकर्ता किया जाएगा। –

+0

@atc, आपका विवरण संदिग्ध है, इसलिए मैं कोड थ्रेड के बिना इसकी थ्रेड सुरक्षा और व्यवहार्यता का आकलन नहीं कर सकता। ध्यान दें कि धारक मुहावरे या तो ताले का उपयोग नहीं करता है (JVM के पास 'LazyHolder' के निर्माण के आसपास एक ताला होगा, लेकिन' getInstance() 'स्वयं सिंक्रनाइज़ नहीं किया गया है)। –

0

वह कोड धागा सुरक्षित नहीं है। यदि एकाधिक धागे फ़ंक्शन निष्पादित करते हैं तो MyObj के कई उदाहरण बनाए जा सकते हैं। आपको सिंक्रनाइज़ेशन के कुछ रूपों की आवश्यकता है।

मौलिक मुद्दा इस ब्लॉक कोड है जो:

if (obj == null) { 
    obj = new MyObj();// costly initialisation 
} 

परमाणु नहीं है। वास्तव में यह परमाणु होने से बहुत लंबा रास्ता है।

1

नहीं, आप अभी भी पहुंच सिंक्रनाइज़ करना होगा। volatile अन्य धागे द्वारा एक धागे द्वारा एक चर में किए गए परिवर्तनों की अनुमति देता है।

  1. obj initally रिक्त है:

    निष्पादन के निम्न प्रवाह (दो धागे टी 1 & टी 2 कल्पना करते हुए) की कल्पना करें।

  2. टी 1: अगर (obj == नल): हाँ
  3. टी 2: यह
  4. टी 2 obj को MyObj का एक नया उदाहरण बनाता है और आवंटित: अगर (obj == नल): हाँ
  5. T1 भी MyObj का एक नया उदाहरण बनाता है और इसे

आप दो बार एक ऑब्जेक्ट बनाते हैं जिसे आप केवल एक बार बनाए जाने की उम्मीद करते हैं। और यह बुरा परिदृश्य नहीं है। आप उस ऑब्जेक्ट को वापस कर सकते हैं जो अब आपके चर के लिए असाइन नहीं किया गया है। (: केवल जावा 5 या नए के साथ, जानकारी के लिए here देखने के काम करता है नोट):

public class DoubleCheckingSingletonExample 
{ 
    private static final Object lockObj = new Object(); 

    private static volatile DoubleCheckingSingletonExample instance; 

    private DoubleCheckingSingletonExample() 
    { 
     //Initialization 
    } 

    public static DoubleCheckingSingletonExample getInstance() 
    { 
     if(instance == null) 
     { 
      synchronized(lockObj) 
      { 
       if(instance == null) 
       { 
        instance = new DoubleCheckingSingletonExample(); 
       } 
      } 
     } 

     return instance; 
    } 
} 

getInstance एक साथ दो धागे से बुलाया जाता है, दोनों पहले होगा

0

फिर भी एक और तरीका यह संभाल करने के लिए दो बार जांच है उदाहरण को शून्य के रूप में देखें, दूसरा सिंक्रनाइज़-ब्लॉक में प्रवेश करता है, और ऑब्जेक्ट को तुरंत चालू करता है। दूसरा उदाहरण देखेंगे अब शून्य नहीं है, और इसे तुरंत चालू करने की कोशिश नहीं करेगा। इंस्टेंस प्राप्त करने के लिए और कॉल देखेंगे कि इंस्टेंस शून्य नहीं है, और बिल्कुल लॉक करने का प्रयास नहीं करेगा।

+0

इस तरह की डबल चेक की गई लॉकिंग को सही तरीके से काम करने के लिए जेडीके 5 की आवश्यकता है: http://cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html –

+2

क्षमा करें, सही लिंक यहां है: http: //www.cs। umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html –

+0

@ डेविड हेफरन: लिंक के लिए धन्यवाद, "जेडीके 5 या नई" - आवश्यकता के बारे में नहीं पता था। मैं इस जानकारी को शामिल करने के लिए उत्तर संपादित कर दूंगा। – esaj

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