44

हेड फर्स्ट डिजाइन पैटर्न पुस्तक से, डबल जाँच की लॉकिंग के साथ सिंगलटन पैटर्न नीचे के रूप में लागू किया गया है ताला लगा:क्यों की इस उदाहरण में अस्थिर प्रयोग किया जाता है डबल जाँच की

public class Singleton { 
    private volatile static Singleton instance; 
    private Singleton() {} 
    public static Singleton getInstance() { 
     if (instance == null) { 
      synchronized (Singleton.class) { 
       if (instance == null) { 
        instance = new Singleton(); 
       } 
      } 
     } 
     return instance; 
    } 
} 

मुझे समझ नहीं आता क्यों volatile किया जा रहा है उपयोग किया गया। volatile उपयोग डबल चेकिंग लॉकिंग i.e प्रदर्शन का उपयोग करने के उद्देश्य को हराने के लिए नहीं करता है?

+4

मैंने सोचा कि डबल चेक लॉकिंग टूटा गया था, क्या किसी ने इसे ठीक किया? –

+4

इसके लायक होने के लिए, मुझे हेड फर्स्ट डिज़ाइन पैटर्न से सीखने के लिए एक भयानक पुस्तक मिल गई। जैसा कि मैंने इसे वापस देखा है, अब यह सही समझ में आता है कि मैंने कहीं और पैटर्न सीख लिया है, लेकिन पैटर्न को जानने के बिना सीखने के लिए यह वास्तव में इसके उद्देश्य को पूरा नहीं करता है। लेकिन यह बहुत लोकप्रिय है, तो शायद यह सिर्फ मुझे घना था। :-) – corsiKa

+0

@ डेविड हेफरनन मैंने इस उदाहरण को एक ऐसे तरीके के रूप में उपयोग किया है जिसमें जेवीएम को डीसीएल करने के लिए भरोसा किया जा सकता है। –

उत्तर

47

volatile क्यों समझने के लिए एक अच्छा संसाधन JCIP पुस्तक से आता है। विकिपीडिया में उस सामग्री के decent explanation भी हैं।

असली समस्या यह है कि Thread Ainstance का निर्माण समाप्त होने से पहले instance के लिए मेमोरी स्पेस असाइन कर सकता है। Thread B उस असाइनमेंट को देखेगा और इसका उपयोग करने का प्रयास करेगा। इसके परिणामस्वरूप Thread B विफल हो रहा है क्योंकि यह instance का आंशिक रूप से निर्मित संस्करण का उपयोग कर रहा है।

+0

ठीक है, यह डीसीएल के साथ अस्थिर तय स्मृति समस्याओं के एक नए कार्यान्वयन की तरह दिखता है। जो भी मुझे अभी भी नहीं मिलता है वह अस्थिरता का उपयोग करने का प्रदर्शन निहितार्थ है। मैंने जो अस्थिर पढ़ा है, उससे सिंक्रनाइज़ेशन जितना धीमा है, तो क्यों न केवल संपूर्ण getInstance() विधि कॉल सिंक्रनाइज़ करें? – toc777

+4

@ toc777 'अस्थिर 'सामान्य दायर से धीमा है। यदि आप प्रदर्शन की तलाश करते हैं, तो धारक-वर्ग पैटर्न के लिए जाएं। 'अस्थिर' यहां केवल यह दिखाने के लिए है कि टूटा हुआ पैटर्न काम करने के लिए * एक तरीका है। यह वास्तविक समस्या की तुलना में एक कोडिंग चुनौती है। – alf

+0

@ आधा बहुत बताता है, धन्यवाद। उन्होंने किताब में बिल्कुल स्पष्ट नहीं किया। – toc777

0

यदि आपके पास यह नहीं है, तो दूसरा थ्रेड सिंक्रनाइज़ किए गए ब्लॉक में पहले सेट के बाद इसे सेट कर सकता है, और आपका स्थानीय कैश अभी भी लगता है कि यह शून्य था।

पहला व्यक्ति शुद्धता के लिए नहीं है (यदि आप सही थे तो यह स्वयं को पराजित करेगा) बल्कि अनुकूलन के लिए।

1

वैरिएबल को volatile के रूप में घोषित करने की गारंटी देता है कि सभी इसका उपयोग वास्तव में स्मृति से वर्तमान मूल्य को पढ़ता है।

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

+3

-1 मैं अपने प्रतिष्ठा अंक आज खो रहा हूँ :) _real_ कारण है, वहाँ स्मृति कैश, धागा की स्थानीय स्मृति के रूप में मॉडलिंग की है।जिस क्रम में स्थानीय मेमोरी को मुख्य रूप से फ़्लश किया जाता है वह अनिर्धारित होता है-अर्थात, जब तक आपके पास * पहले से * संबंध नहीं होते हैं, उदाहरण के लिए, 'अस्थिर' का उपयोग करके। रजिस्टरों के पास अपूर्ण वस्तुओं और डीसीएल समस्या से कोई लेना देना नहीं है। – alf

+3

'अस्थिरता' की आपकी परिभाषा बहुत संकीर्ण है - अगर वह सब अस्थिर था, तो डबल चेक लॉकिंग Voo

+1

@ टिमबेंडर अगर सिंगलटन _contains_ mutable स्थिति है, तो इसे फ्लश करने के लिए सिंगलटन के संदर्भ में कुछ भी नहीं है (ठीक है, एक अप्रत्यक्ष लिंक है, एक सिंगलटन के 'वोल्टाली' संदर्भ तक पहुंचने से आपका धागा मुख्य स्मृति को फिर से पढ़ता है-लेकिन यह एक माध्यमिक प्रभाव है, समस्या का कारण नहीं :) :) – alf

4

ठीक है, प्रदर्शन के लिए कोई डबल-चेक लॉकिंग नहीं है। यह टूटा पैटर्न है।

छोड़कर भावनाओं तरफ, volatile समय दूसरा धागा गुजरता instance == null द्वारा क्योंकि इसके बिना यहाँ है, पहले धागा अभी तक new Singleton() का निर्माण नहीं हो सकता है: कोई भी वादा करता है कि वस्तु के निर्माण के किसी भी थ्रेड के लिए होता है-पहले काम instance के लिए, लेकिन वास्तव में वस्तु बनाते हैं।

volatile बदले में होता है पढ़ने और लिखने के बीच संबंध, और टूटे पैटर्न को ठीक करता है।

यदि आप प्रदर्शन की तलाश में हैं, तो इसके बजाय धारक आंतरिक स्थिर वर्ग का उपयोग करें।

+0

फिक्स्ड, धन्यवाद @ कोपोल! – alf

1

एक अस्थिर पढ़ना वास्तव में महंगा नहीं है।

आप एक अस्थिर पढ़ने के प्रभाव का निरीक्षण करने के लिए, एक तंग लूप में getInstance() पर कॉल करने के लिए एक परीक्षण तैयार कर सकते हैं; हालांकि यह परीक्षण यथार्थवादी नहीं है; ऐसी स्थिति में, प्रोग्रामर आमतौर पर getInstance() पर कॉल करेगा और उपयोग की अवधि के लिए उदाहरण कैश करेगा।

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

9

जैसा कि @irreputable द्वारा उद्धृत किया गया है, अस्थिर महंगा नहीं है। यहां तक ​​कि यदि यह महंगा है, तो प्रदर्शन पर स्थिरता को प्राथमिकता दी जानी चाहिए।

आलसी सिंगलेट्स के लिए एक और साफ सुरुचिपूर्ण तरीका है।

public final class Singleton { 
    private Singleton() {} 
    public static Singleton getInstance() { 
     return LazyHolder.INSTANCE; 
    } 
    private static class LazyHolder { 
     private static final Singleton INSTANCE = new Singleton(); 
    } 
} 

स्रोत लेख: Initialization-on-demand_holder_idiom विकिपीडिया

से सॉफ्टवेयर इंजीनियरिंग में, मांग धारक पर प्रारंभ (डिजाइन पैटर्न) मुहावरा एक आलसी-लोडेड सिंगलटन है। जावा के सभी संस्करणों में, मुहावरा अच्छा प्रदर्शन

के साथ एक सुरक्षित, अत्यधिक समवर्ती आलसी आरंभीकरण

के बाद से कक्षा प्रारंभ करने में किसी भी static चर नहीं है, प्रारंभ तुच्छता से पूरा करता है सक्षम बनाता है।

स्थिर श्रेणी परिभाषा LazyHolder इसके भीतर प्रारंभ नहीं की जाती है जब तक कि JVM निर्धारित करता है कि LazyHolder को निष्पादित किया जाना चाहिए।

स्थिर वर्ग LazyHolder केवल जब स्थिर विधि getInstance वर्ग सिंगलटन पर शुरू हो जाती है निष्पादित किया जाता है, और पहली बार ऐसा होता है JVM लोड और LazyHolder वर्ग प्रारंभ हो जाएगा।

यह समाधान विशेष भाषा संरचनाओं (यानी volatile या synchronized) की आवश्यकता के बिना थ्रेड-सुरक्षित है।