2009-10-19 9 views
16

मैंने इस बारे में सुना है कि गैर-थ्रेड-सुरक्षित कोड में अनुचित रूप से ऑब्जेक्ट्स का निर्माण किया गया है, लेकिन मेरे पास वास्तव में गोएट्ज़ की पुस्तक में पढ़ने के बाद भी अवधारणा नहीं है। मैं इस कोड की गंध की अपनी समझ को मजबूत करना चाहता हूं क्योंकि मैं इसे कर रहा हूं और इसे महसूस भी नहीं कर रहा हूं। कृपया इसे छूने के लिए अपनी स्पष्टीकरण में कोड प्रदान करें, धन्यवाद।"यह" जावा में कन्स्ट्रक्टर से कैसे बचता है?

उत्तर

15

सच सरल उदाहरण:

public class Test 
{ 
    private static Test lastCreatedInstance; 

    public Test() 
    { 
     lastCreatedInstance = this; 
    } 
} 
+1

यह ऐसा कुछ नहीं दिखता जो सामान्य रूप से होता है, यहां तक ​​कि मैं इसे लिखने के बारे में भी नहीं सोचूंगा। लेकिन बचने के भाग हालांकि मुझे स्पष्ट है। इस कोड का संभावित परिणाम क्या होगा? –

+0

@non sequitor: कल्पना करें कि कन्स्ट्रक्टर में बहुत से अन्य प्रारंभिकरण हैं ... लेकिन एक और थ्रेड कन्स्ट्रक्टर समाप्त होने से पहले 'lastCreatedInstance' का उपयोग करता है। यह आधा प्रारंभिक वस्तु का उपयोग कर सकता है। –

21

उदाहरण: एक कन्स्ट्रक्टर में, आप एक ईवेंट श्रोता आंतरिक कक्षा (यह वर्तमान ऑब्जेक्ट का निहित संदर्भ है) बनाते हैं, और इसे श्रोता की सूची में पंजीकृत करते हैं।
=> तो आपकी वस्तु का उपयोग किसी अन्य थ्रेड द्वारा किया जा सकता है, भले ही यह अपने कन्स्ट्रक्टर को निष्पादित नहीं करता है।

 public class A { 

     private boolean isIt; 
     private String yesItIs; 

     public A() { 
     EventListener el = new EventListener() { ....}; 
     StaticListeners.register(el); 
     isIt = true; 
     yesItIs = "yesItIs"; 
     } 
    } 

एक अतिरिक्त समस्या यह है कि बाद में भी हो सकता है: वस्तु एक पूरी तरह से बनाया जा सकता है, एक और धागा द्वारा उपयोग के सभी थ्रेड के लिए उपलब्ध कराया, ... सिवाय इसके कि कि धागा एक उदाहरण देख सकते हैं के रूप में बनाया , हाँ इसके साथ "yesItIs" मान है, लेकिन isIt नहीं! विश्वास करो या नहीं, यह हो सकता है! क्या है:

=>तुल्यकालन धागा अवरुद्ध करने के बारे में केवल आधा है, दूसरे आधे अंतर-धागा दृश्यता बारे में है।

कि जावा चुनाव के लिए कारण प्रदर्शन है: अंतर-धागा दृश्यता प्रदर्शन मार डालेंगे सभी डेटा, सभी धागे के साथ साझा किया जाएगा यदि ऐसा है तो केवल सिंक्रनाइज़ डेटा साझा करने के लिए गारंटी है ...

+0

आप एक स्थिर परिवर्तनशील रहे हैं, तो आप पहले से ही समस्याएं हैं। एक और यथार्थवादी मामला यह है कि आप कन्स्ट्रक्टर को दिए गए तर्क के लिए एक संदर्भ (उदाहरण के लिए, श्रोता के माध्यम से) जोड़ते हैं। –

+0

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

5

यही कारण है कि डबल-चेक लॉकिंग काम नहीं करती है। क्योंकि स्थानीय चर के लिए काम निर्माण (निर्माता या कारखाने विधि) के आराम से पहले हो सकता है अनुभवहीन कोड

if(obj == null) 
{ 
    synchronized(something) 
    { 
    if (obj == null) obj = BuildObject(...); 
    } 
} 
// do something with obj 

सुरक्षित नहीं है। इस प्रकार थ्रेड 1 BuildObject चरण में हो सकता है, जब थ्रेड 2 एक ही ब्लॉक में प्रवेश करता है, तो एक गैर-शून्य obj का पता लगाता है, और उसके बाद एक अपूर्ण वस्तु (थ्रेड 1 को मध्य-कॉल में निर्धारित किया गया) पर संचालित करने के लिए आगे बढ़ता है।

+0

क्या आप डाउन-वोट समझाएंगे। डबल-चेक लॉकिंग "इस" संदर्भ से बचने का क्लासिक उदाहरण है। –

+0

केवल यह उदाहरण इसलिए नहीं है क्योंकि 'Buildjbject' ने अपने कन्स्ट्रक्टर को पूरा नहीं किया है, तब तक 'obj' को कभी भी असाइन नहीं किया जाता है। – Bombe

+2

यह वह जगह है जहां आप गलत हैं - निर्माण पूर्ण होने से पहले असाइनमेंट हो सकता है। पूर्ण चर्चा के लिए http://www.ibm.com/developerworks/java/library/j-dcl.html देखें। –

4
public class MyClass{ 
    String name;  

    public MyClass(String s) 
    { 
     if(s==null) 
     { 
      throw new IllegalArgumentException(); 
     } 
     OtherClass.method(this); 
     name= s; 
    } 

    public getName(){ return name; } 
} 

उपरोक्त कोड में, OtherClass.method()MyClass का एक उदाहरण है जो उस बिंदु पर है अधूरे निर्माण पारित हो जाता है, यानि कि अभी तक अनुबंध है कि name संपत्ति में गैर-शून्य है को पूरा नहीं कर।

2

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

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

में थोड़ा और अधिक स्पष्टीकरण:

आपका निर्माता अपनी कक्षा में हर क्षेत्र को प्रारंभ कर सकते हैं, लेकिन अगर आप की अनुमति देने के लिए 'इस' से बचने के लिए पहले अन्य वस्तुओं के किसी भी बनाई गई हैं, वे अशक्त हो सकता है (या डिफ़ॉल्ट आदिम) जब अन्य थ्रेड द्वारा देखी अगर 1. वे अंतिम घोषित नहीं कर रहे हैं या 2. वे अस्थिर घोषित नहीं कर रहे हैं

+0

डबल-चेक लॉकिंग अब टूटा नहीं है। यदि चर अस्थिर है, * और कोड सही ढंग से संरचित है, * यह जावा 5+ मेमोरी मॉडल के तहत ठीक काम करता है। आलसी शुरुआत के बाद, "लागत" सिर्फ एक अस्थिर पढ़ा जाता है। – erickson

+0

हां वैरिएबल को अस्थिर जोड़ना इसे सही ढंग से प्रारंभ करने की अनुमति देगा, लेकिन इससे इसे अंतिम घोषित किया जाएगा। लेकिन गोएट्ज ने नोट किया कि इसे अस्थिर घोषित करने के बावजूद यह 100% पूर्ण प्रमाण नहीं है। क्या होगा, उदाहरण के लिए, अस्थिर वस्तु में ऐसे क्षेत्र होते हैं जिन्हें इसके निर्माता में घोषित किया जाता है। आपके पास एक अस्थिर ऑब्जेक्ट myObject होगा, जिसे कन्स्ट्रक्टर के भीतर शुरू किया गया है। और एक और धागा myObject.getSomeField() प्राप्त करता है। SomOtherField() प्राप्त करें। अगर कुछ फोल्ड के कन्स्ट्रक्टर में कुछ अन्य फ़ील्ड घोषित किया गया है, तो यह संभवतः शून्य हो सकता है जबकि कुछ अन्यथा आंशिक रूप से –

+0

का निर्माण किया जा सकता है एक निर्माता में धागा बनाना एक अच्छा उदाहरण है। –

0
public class Test extends SomeUnknownClass{ 
    public Test(){ 
     this.addListner(new SomeEventListner(){ 
      @Override 
      void act(){} 
     }); 
    } 
} 

बाद SomeEventListner की इस कार्रवाई को instanse वस्तु का परीक्षण करने के लिए एक लिंक होगा एक सामान्य भीतरी वर्ग के रूप में।

अधिक उदाहरण यहाँ मिलेगा जा सकता है: http://www.ibm.com/developerworks/java/library/j-jtp0618/index.html