जावा सिंगलटन और सिंक्रोनाइज़ेशन


117

कृपया सिंगलटन और मल्टीथ्रेडिंग के बारे में मेरे प्रश्नों को स्पष्ट करें:

  • एक बहुपरत वातावरण में जावा में सिंगलटन को लागू करने का सबसे अच्छा तरीका क्या है?
  • क्या होता है जब एक getInstance() ही समय पर कई थ्रेड विधि तक पहुंचने का प्रयास करते हैं ?
  • क्या हम सिंगलटन बना सकते हैं getInstance() synchronized?
  • क्या सिंग्लटन कक्षाओं का उपयोग करते समय सिंक्रनाइज़ेशन की वास्तव में आवश्यकता है?

जवाबों:


211

हाँ, यह आवश्यक है। कई तरीके हैं जिनसे आप आलसी इनिशियलाइज़ेशन के साथ थ्रेड सेफ्टी हासिल कर सकते हैं:

ड्रेकोनियन तुल्यकालन:

private static YourObject instance;

public static synchronized YourObject getInstance() {
    if (instance == null) {
        instance = new YourObject();
    }
    return instance;
}

इस समाधान के लिए आवश्यक है कि हर धागे को तब सिंक्रनाइज़ किया जाए जब वास्तव में केवल पहले कुछ की आवश्यकता हो।

दोहरा चेक सिंक्रनाइज़ेशन :

private static final Object lock = new Object();
private static volatile YourObject instance;

public static YourObject getInstance() {
    YourObject r = instance;
    if (r == null) {
        synchronized (lock) {    // While we were waiting for the lock, another 
            r = instance;        // thread may have instantiated the object.
            if (r == null) {  
                r = new YourObject();
                instance = r;
            }
        }
    }
    return r;
}

यह समाधान सुनिश्चित करता है कि केवल पहले कुछ धागे जो आपके सिंगलटन का अधिग्रहण करने का प्रयास करते हैं, उन्हें लॉक प्राप्त करने की प्रक्रिया से गुजरना होगा।

मांग पर आरंभ :

private static class InstanceHolder {
    private static final YourObject instance = new YourObject();
}

public static YourObject getInstance() {
    return InstanceHolder.instance;
}

यह समाधान थ्रेड सुरक्षा सुनिश्चित करने के लिए वर्ग स्मृति के बारे में जावा मेमोरी मॉडल की गारंटी का लाभ उठाता है। प्रत्येक वर्ग को केवल एक बार लोड किया जा सकता है, और इसे केवल तब लोड किया जाएगा जब इसकी आवश्यकता होगी। इसका मतलब है कि पहली बार getInstanceबुलाया गया है, InstanceHolderलोड किया instanceजाएगा और बनाया जाएगा, और चूंकि यह ClassLoaderएस द्वारा नियंत्रित किया जाता है , इसलिए कोई अतिरिक्त सिंक्रनाइज़ेशन आवश्यक नहीं है।


23
चेतावनी - दोहरे-चेक सिंक्रनाइज़ेशन के साथ सावधान रहें। यह प्री-जावा 5 जेवीएम के साथ मेमोरी मॉडल के "मुद्दों" के कारण ठीक से काम नहीं करता है।
स्टीफन सी

3
-1 Draconian synchronizationऔर Double check synchronizationgetInstance () - तरीका स्टैटिक होना चाहिए!
ग्रिम

2
@PeterRader वे नहीं करते की जरूरत होने के लिए static, लेकिन इसे और अधिक समझ कर सकता है अगर वे थे। अनुरोध के अनुसार संशोधित।
जेफरी

4
आप डबल चेक किए गए लॉकिंग का कार्यान्वयन कर रहे हैं काम करने की गारंटी नहीं है। यह वास्तव में आपको डबल चेकिंग लॉकिंग के लिए उद्धृत लेख में समझाया गया है। :) वहाँ एक उदाहरण है जिसमें अस्थिर का उपयोग किया जाता है जो 1.5 और उच्चतर के लिए ठीक से काम करता है (डबल चेक किया गया लॉकिंग केवल 1.5 के नीचे सादा टूट गया है)। मांग धारक पर आरंभिक विवरण भी लेख में उद्धृत किया गया है जो संभवतः आपके उत्तर में एक सरल समाधान होगा।
15'15

2
@MediumOne AFAIK, rशुद्धता के लिए आवश्यक नहीं है। यह अस्थिर क्षेत्र तक पहुँचने से बचने के लिए सिर्फ एक अनुकूलन है, क्योंकि यह स्थानीय चर तक पहुँचने से कहीं अधिक महंगा है।
जेफरी

69

यह पैटर्न स्पष्ट सिंक्रनाइज़ेशन के बिना उदाहरण के एक थ्रेड-सुरक्षित आलसी-आरंभीकरण करता है !

public class MySingleton {

     private static class Loader {
         static final MySingleton INSTANCE = new MySingleton();
     }

     private MySingleton () {}

     public static MySingleton getInstance() {
         return Loader.INSTANCE;
     }
}

यह काम करता है क्योंकि यह आपके लिए सभी सिंक्रनाइज़ेशन को मुफ्त में करने के लिए क्लास लोडर का उपयोग करता है: क्लास MySingleton.Loaderको पहले getInstance()विधि के अंदर एक्सेस किया जाता है, इसलिए पहली बार कॉल करने पर Loaderक्लास लोड होता getInstance()है। इसके अलावा, क्लास लोडर इस बात की गारंटी देता है कि क्लास में पहुँचने से पहले सभी स्टैटिक इनिशियलाइज़ेशन पूरे हो जाएँ - यही आपको थ्रेड-सेफ्टी देता है।

यह जादू जैसा है।

यह वास्तव में झुरताडो के एनम पैटर्न के समान है, लेकिन मैं एनम पैटर्न को एनम अवधारणा का दुरुपयोग मानता हूं (हालांकि यह काम करता है)


11
सिंक्रोनाइज़ेशन अभी भी मौजूद है, यह सिर्फ प्रोग्रामर द्वारा JVM द्वारा लागू किया गया है।
जेफरी

आप @Jeffrey निश्चित रूप से रहे हों तो सही - मैं यह सब में (संपादन देखें) टाइप किया गया था
बोहेमियन

2
मैं समझता हूं कि इससे जेवीएम पर कोई फर्क नहीं पड़ता है, मैं सिर्फ यह कह रहा हूं कि यह मेरे लिए एक अंतर है जहां तक ​​स्व-दस्तावेज कोड जाता है। मैंने अभी तक (या enum) पहले "अंतिम" कीवर्ड के बिना जावा में सभी कैप कभी नहीं देखे हैं, थोड़ा संज्ञानात्मक असंगति मिली। जावा को पूरे समय प्रोग्रामिंग करने वाले लोगों के लिए, शायद इससे कोई फर्क नहीं पड़ेगा, लेकिन अगर आप भाषाओं को आगे-पीछे करते हैं, तो यह स्पष्ट होने में मदद करता है। न्यूटॉब्स के लिए डिट्टो। हालांकि, मुझे यकीन है कि कोई भी इस शैली को बहुत जल्दी से अनुकूलित कर सकता है; सभी कैप शायद पर्याप्त हैं। नाइट पिक लेने का मतलब यह नहीं है, मुझे आपकी पोस्ट पसंद आई।
रूबी

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

2
@ wz366 वास्तव में, हालांकि यह आवश्यक नहीं है, मैं स्टाइल कारणों से सहमत हूं (क्योंकि यह प्रभावी रूप से अंतिम है क्योंकि कोई अन्य कोड इसे एक्सेस नहीं कर सकता है) finalजोड़ा जाना चाहिए। किया हुआ।
बोहेमियन

21

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

public enum Singleton {
    SINGLE;
    public void myMethod(){  
    }
}

और फिर बस अपने धागे अपने उदाहरण का उपयोग करें:

Singleton.SINGLE.myMethod();

8

हां, आपको getInstance()सिंक्रनाइज़ करने की आवश्यकता है । यदि यह नहीं है, तो ऐसी स्थिति उत्पन्न हो सकती है जहां कक्षा के कई उदाहरण बन सकते हैं।

उस मामले पर विचार करें जहां आपके पास दो धागे हैं जो getInstance()एक ही समय में कॉल करते हैं। अब कल्पना कीजिए कि T1 पिछले instance == nullचेक को निष्पादित करता है , और फिर T2 चलता है। इस बिंदु पर समय पर इंस्टेंस नहीं बनाया जाता है या सेट नहीं किया जाता है, इसलिए टी 2 चेक को पास करेगा और इंस्टेंस बनाएगा। अब कल्पना करें कि निष्पादन T1 पर वापस आ जाता है। अब सिंगलटन बनाया गया है, लेकिन टी 1 पहले ही जांच कर चुका है! यह फिर से वस्तु बनाने के लिए आगे बढ़ेगा! getInstance()सिंक्रोनाइज़ करना इस समस्या को रोकता है।

सिंगलेट्स को धागा-सुरक्षित बनाने के कुछ तरीके हैं, लेकिन getInstance()सिंक्रनाइज़ करना संभवत: सबसे सरल है।


पूरे विधि तुल्यकालन बनाने के बजाय, सिंक्रोनाइज़्ड ब्लॉक में ऑब्जेक्ट क्रिएशन कोड डालने से मदद मिलेगी?
रिकैविस

@RaoG No. आप सिंक्रनाइज़ेशन ब्लॉक में चेक और निर्माण दोनों चाहते हैं । आपको उन दो ऑपरेशनों को एक साथ बिना किसी रुकावट के होने की जरूरत है या जो स्थिति मैंने ऊपर वर्णित की है वह हो सकती है।
ओलेक्सी

7

एनम सिंगलटन

एक सिंगलटन को लागू करने का सबसे सरल तरीका जो थ्रेड-सुरक्षित है, एक एनम का उपयोग कर रहा है

public enum SingletonEnum {
  INSTANCE;
  public void doSomething(){
    System.out.println("This is a singleton");
  }
}

यह कोड जावा 1.5 में Enum की शुरुआत के बाद से काम करता है

डबल चेकिंग लॉकिंग

यदि आप एक "क्लासिक" सिंगलटन को कोड करना चाहते हैं जो एक बहुपरत वातावरण (जावा 1.5 से शुरू) में काम करता है, तो आपको इस का उपयोग करना चाहिए।

public class Singleton {

  private static volatile Singleton instance = null;

  private Singleton() {
  }

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

यह 1.5 से पहले थ्रेड-सुरक्षित नहीं है क्योंकि अस्थिर कीवर्ड का कार्यान्वयन अलग था।

प्रारंभिक लोडिंग सिंगलटन (जावा 1.5 से पहले भी काम करता है)

जब क्लास लोड होती है और थ्रेड सुरक्षा प्रदान करता है, तो यह कार्यान्वयन सिंगलटन को त्वरित करता है।

public class Singleton {

  private static final Singleton instance = new Singleton();

  private Singleton() {
  }

  public static Singleton getInstance() {
    return instance;
  }

  public void doSomething(){
    System.out.println("This is a singleton");
  }

}

2

आप क्लास लोड पर आवृत्ति को रोकने और थ्रेड सिंक्रोनाइज़ेशन समस्याओं को रोकने के लिए स्थैतिक कोड ब्लॉक का भी उपयोग कर सकते हैं।

public class MySingleton {

  private static final MySingleton instance;

  static {
     instance = new MySingleton();
  }

  private MySingleton() {
  }

  public static MySingleton getInstance() {
    return instance;
  }

}

@ विमा एक जोड़ी अन्य बातें। 1. आपको instanceफाइनल करना चाहिए । आपको getInstance()स्टैटिक बनाना चाहिए ।
जॉन विंट

यदि आप सिंगलटन में एक धागा बनाना चाहते हैं तो आप क्या करेंगे।
अरुण जॉर्ज

@ arun-george यदि आवश्यक हो तो थ्रेड पूल, सिंगल थ्रेड पूल का उपयोग करें, और थोड़ी देर (सत्य) -ट्री-कैच-थ्रैबल के साथ घेरें, यदि आप सुनिश्चित करना चाहते हैं कि आपका थ्रेड कभी नहीं मरता है, तो कोई भी त्रुटि नहीं है?
tgkprog

0

एक बहुपरत वातावरण में जावा में सिंगलटन को लागू करने का सबसे अच्छा तरीका क्या है?

सिंगलटन को लागू करने के सर्वोत्तम तरीके के लिए इस पोस्ट का संदर्भ लें।

जावा में सिंगलटन पैटर्न को लागू करने का एक प्रभावी तरीका क्या है?

क्या होता है जब एक ही समय में कई सूत्र getInstance () पद्धति तक पहुंचने का प्रयास करते हैं?

यह आपके द्वारा लागू की गई विधि पर निर्भर करता है। यदि आप अस्थिर चर के बिना डबल लॉकिंग का उपयोग करते हैं, तो आप आंशिक रूप से निर्मित सिंगलटन ऑब्जेक्ट प्राप्त कर सकते हैं।

अधिक जानकारी के लिए इस प्रश्न का संदर्भ लें:

डबल चेक्ड लॉकिंग के इस उदाहरण में अस्थिरता का उपयोग क्यों किया जाता है

क्या हम सिंगलटन के getInstance () को सिंक्रनाइज़ कर सकते हैं?

क्या सिंग्लटन कक्षाओं का उपयोग करते समय सिंक्रनाइज़ेशन की वास्तव में आवश्यकता है?

नीचे दिए गए तरीकों से यदि आप सिंगलटन को लागू करते हैं तो आवश्यक नहीं है

  1. स्थैतिक घुसपैठ
  2. enum
  3. प्रारंभिक-ऑन-डिमांड_होल्डर_डायोम के साथ LazyInitalaization

इस प्रश्न को देखें और अधिक विवरण

जावा सिंगलटन डिज़ाइन पैटर्न: प्रश्न


0
public class Elvis { 
   public static final Elvis INSTANCE = new Elvis();
   private Elvis () {...}
 }

स्रोत: प्रभावी जावा -> आइटम 2

यह इसका उपयोग करने का सुझाव देता है, यदि आप सुनिश्चित हैं कि वर्ग हमेशा सिंगलटन रहेगा।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.