2010-03-25 15 views
17

गोएट्ज़ का Java Concurrency in Practice, पृष्ठ 41, उल्लेख करता है कि this संदर्भ निर्माण के दौरान बच सकता है। एक उदाहरण "यह मत करो":"अपूर्ण रूप से निर्मित वस्तु" क्या है?

public class ThisEscape { 
    public ThisEscape(EventSource source) { 
     source.registerListener(
      new EventListener() { 
       public void onEvent(Event e) { 
        doSomething(e); 
       } 
      }); 
    } 
} 

यहाँ this तथ्य यह है कि doSomething(e) enclosing ThisEscape उदाहरण के लिए संदर्भित करता माध्यम से "से बचने" है। सार्वजनिक कन्स्ट्रक्टर (सभी काम करने) के बजाय स्थैतिक फैक्ट्री विधियों का उपयोग करके स्थिति तय की जा सकती है (पहले सादे ऑब्जेक्ट का निर्माण करें, फिर श्रोता को पंजीकृत करें)। पुस्तक जारी है:

अपने कन्स्ट्रक्टर के भीतर से एक ऑब्जेक्ट प्रकाशित करना एक अपूर्ण रूप से निर्मित ऑब्जेक्ट प्रकाशित कर सकता है। यह सच है भले ही प्रकाशन निर्माता में अंतिम विवरण है। यदि this संदर्भ निर्माण के दौरान बच निकलता है, तो वस्तु को पर सही ढंग से निर्मित नहीं किया जाता है।

मुझे यह काफी नहीं मिलता है। यदि प्रकाशन निर्माता में आखिरी बयान है, तो क्या इससे पहले सभी निर्माण कार्य नहीं किए गए हैं? this तब तक मान्य नहीं है? जाहिर है उसके बाद कुछ वूडू चल रहा है, लेकिन क्या?

उत्तर

15

अंतिम क्षेत्र के संबंध में, कन्स्ट्रक्टर के अंत में एक कन्स्ट्रक्टर का अंत एक विशेष स्थान है। जावा भाषा विशिष्टता की section 17.5 से:

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

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

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

क्या हो रहा है, मुझे संदेह है कि एक निर्माता के बहुत अंत में एक अंतर्निहित स्मृति बाधा है, यह सुनिश्चित कर लें कि सभी धागे नए डेटा को "देखें"; उस मेमोरी बाधा को लागू किए बिना, समस्याएं हो सकती हैं।

+0

वाह, यह आश्चर्य की बात है कि 'अंतिम' फ़ील्ड, जिन्हें आम तौर पर समवर्ती मित्र माना जाता है, इस मामले में अपराधी हैं! –

+0

@ जुनास: यह रगड़ है - वे समेकन-अनुकूल हैं * यदि आप सुनिश्चित करते हैं कि संदर्भ कन्स्ट्रक्टर * से नहीं बचता है। ज्यादातर मामलों में भुगतान करने के लिए एक बहुत ही कम कीमत है। –

+1

असल में, यह किसी भी फ़ील्ड पर लागू होता है, न केवल अंतिम वाले। –

2

रजिस्टर लिस्टनर समाप्त होने और कन्स्ट्रक्टर लौटने के बीच एक छोटा लेकिन सीमित समय है। उस समय एक और धागा उपयोग कर सकता था और कुछ करने का प्रयास कर सकता था()। यदि उस समय रनटाइम सीधे आपके कोड पर वापस नहीं आया, तो ऑब्जेक्ट अमान्य स्थिति में हो सकता है।

मुझे वास्तव में जावा का यकीन नहीं है, लेकिन एक उदाहरण मैं सोच सकता हूं कि संभवतः रनटाइम आपके पास लौटने से पहले उदाहरण को स्थानांतरित करता है।

यह आपको एक छोटा मौका देता है जो मैं आपको देता हूं।

6

जब आप इस एस्केप को उपclass करते हैं तो एक और समस्या उत्पन्न होती है, और बाल वर्ग इस संरक्षक को आमंत्रित करता है। EventListener में इस संदर्भ में अंतर्निहित एक अपूर्ण रूप से निर्मित वस्तु होगी।

+1

अच्छी कॉल। विशेष रूप से यह समस्याएं पैदा कर सकता है यदि सबक्लास 'इस ऐस्केप' से वर्चुअल विधियों को ओवरराइड करता है - उन ओवरराइड विधियों को राज्य की आवश्यकता होने से पहले बुलाया जा सकता है। –

+1

यह सच है, लेकिन ऑफ-विषय। –

+1

@ स्टीफन सी: मुझे लगता है कि यह काफी अच्छा मुद्दा है, निश्चित रूप से ऑफ-विषय नहीं। –

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