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


812

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


1
"जावा में सिंगलटन पैटर्न को लागू करने का एक प्रभावी तरीका क्या है?" कृपया कुशल परिभाषित करें।
Marian Pa16dzioch

medium.com/@kevalpatel2106/… । यह सिंगलटन पैटर्न में धागा, प्रतिबिंब और क्रमिक सुरक्षा कैसे प्राप्त करें, इस पर पूरा लेख है। यह सिंगलटन वर्ग के लाभों और सीमाओं को समझने का अच्छा स्रोत है।
पटेल

जैसा कि यहोशू बलोच प्रभावी जावा में बताते हैं, एनम सिंगलटन जाने का सबसे अच्छा तरीका है। यहाँ मैंने विभिन्न कार्यान्वयनों को आलसी / उत्सुक आदि के रूप में वर्गीकृत किया है
isaolmez

जवाबों:


782

एक एनम का प्रयोग करें:

public enum Foo {
    INSTANCE;
}

जोशुआ बलोच ने Google I / O 2008 में अपने प्रभावी जावा रीलोडेड टॉक: वीडियो के लिंक में इस दृष्टिकोण को समझाया । इसके अलावा स्लाइड देख उसकी प्रस्तुति (के 30-32 effective_java_reloaded.pdf ):

एक सीरियल सिंगनल को लागू करने का सही तरीका

public enum Elvis {
    INSTANCE;
    private final String[] favoriteSongs =
        { "Hound Dog", "Heartbreak Hotel" };
    public void printFavorites() {
        System.out.println(Arrays.toString(favoriteSongs));
    }
}

संपादित करें: "प्रभावी जावा" का एक ऑनलाइन भाग कहता है:

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


202
मुझे लगता है कि लोगों को एक विशेषता के साथ सिर्फ एक वर्ग के रूप में enums देखना शुरू करना चाहिए। यदि आप संकलन समय पर अपनी कक्षा के उदाहरणों को सूचीबद्ध कर सकते हैं, तो एनम का उपयोग करें।
अमीर अरद

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

3
नमस्ते, क्या कोई मुझे बता सकता है कि इस प्रकार के सिंगलटन का परीक्षण के मामलों में कैसे मजाक और परीक्षण किया जा सकता है। मैं इस प्रकार के लिए नकली सिंगलटन उदाहरण स्वैप करने की कोशिश की, लेकिन नहीं कर सका।
आशीष शर्मा

29
मुझे लगता है कि यह समझ में आता है, लेकिन मुझे अभी भी यह पसंद नहीं है। आप एक सिंगलटन कैसे बनाएंगे जो दूसरी कक्षा का विस्तार करता है? यदि आप एक एनम का उपयोग करते हैं, तो आप नहीं कर सकते।
छेरवे

11
@ बीवीडीबी: यदि आप बहुत सारे लचीलेपन चाहते हैं, तो आप पहले से ही एक सिंगलटन को लागू करके खराब कर चुके हैं। जरूरत पड़ने पर एक स्वतंत्र उदाहरण बनाने की क्षमता अपने आप में अनमोल है।
cHao

233

उपयोग के आधार पर, कई "सही" उत्तर हैं।

Java5 के बाद से इसे करने का सबसे अच्छा तरीका एक एनम का उपयोग करना है:

public enum Foo {
   INSTANCE;
}

प्री जावा 5, सबसे सरल मामला है:

public final class Foo {

    private static final Foo INSTANCE = new Foo();

    private Foo() {
        if (INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return INSTANCE;
    }

    public Object clone() throws CloneNotSupportedException{
        throw new CloneNotSupportedException("Cannot clone instance of this class");
    }
}

कोड पर चलते हैं। पहला, आप चाहते हैं कि कक्षा अंतिम हो। इस मामले में, मैंने finalउपयोगकर्ताओं को यह बताने के लिए कीवर्ड का उपयोग किया है कि यह अंतिम है। फिर आपको उपयोगकर्ताओं को अपना फू बनाने के लिए रोकने के लिए कंस्ट्रक्टर को निजी बनाना होगा। कंस्ट्रक्टर से एक अपवाद को फेंकना उपयोगकर्ताओं को दूसरा फू बनाने के लिए प्रतिबिंब का उपयोग करने से रोकता है। तब आप private static final Fooकेवल उदाहरण के लिए एक फ़ील्ड बनाते हैं , और public static Foo getInstance()इसे वापस करने के लिए एक विधि। जावा विनिर्देश यह सुनिश्चित करता है कि निर्माणकर्ता को केवल तब बुलाया जाता है जब कक्षा पहली बार उपयोग की जाती है।

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

आप private static classउदाहरण लोड करने के लिए a का उपयोग कर सकते हैं । फिर कोड इस तरह दिखेगा:

public final class Foo {

    private static class FooLoader {
        private static final Foo INSTANCE = new Foo();
    }

    private Foo() {
        if (FooLoader.INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }
}

चूंकि लाइन private static final Foo INSTANCE = new Foo();को केवल तब निष्पादित किया जाता है जब क्लास फूलाडेर का उपयोग वास्तव में किया जाता है, यह आलसी तात्कालिकता का ख्याल रखता है, और क्या यह थ्रेड सुरक्षित होने की गारंटी है।

जब आप भी अपनी वस्तु को क्रमबद्ध करने में सक्षम होना चाहते हैं, तो आपको यह सुनिश्चित करने की आवश्यकता है कि deserialization एक प्रतिलिपि नहीं बनाएगा।

public final class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    private static class FooLoader {
        private static final Foo INSTANCE = new Foo();
    }

    private Foo() {
        if (FooLoader.INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }

    @SuppressWarnings("unused")
    private Foo readResolve() {
        return FooLoader.INSTANCE;
    }
}

विधि readResolve()सुनिश्चित करेगी कि एकमात्र उदाहरण वापस कर दिया जाएगा, तब भी जब ऑब्जेक्ट को आपके प्रोग्राम के पिछले भाग में क्रमबद्ध किया गया था।


32
प्रतिबिंब के लिए जाँच बेकार है। यदि अन्य कोड निजीकरण पर प्रतिबिंब का उपयोग कर रहा है, तो यह गेम ओवर है। इस तरह के दुरुपयोग के तहत सही ढंग से काम करने की कोशिश करने का कोई कारण नहीं है। और अगर आप कोशिश करते हैं, तो यह एक अधूरा "संरक्षण" होगा, वैसे भी, बहुत सारे व्यर्थ कोड।
Wouter Coekaerts

5
> "सबसे पहले, आप चाहते हैं कि कक्षा अंतिम हो"। क्या कोई इस पर विस्तार से बता सकता है?
प्लेग हैमर 21

2
Deserialisation सुरक्षा पूरी तरह से टूट गया है (मुझे लगता है कि यह प्रभावी जावा 2 एड में उल्लिखित है)।
टॉम हॉल्टिन -

9
-1 यह बिल्कुल सरल मामला नहीं है, यह आकस्मिक और अनावश्यक रूप से जटिल है। जोनाथन के जवाब को वास्तव में सबसे सरल समाधान के लिए देखें जो सभी मामलों में 99.9% में पर्याप्त है।
माइकल बोर्गवर्ड

2
यह तब उपयोगी होता है जब आपके सिंगलटन को सुपरक्लास से विरासत में प्राप्त करने की आवश्यकता होती है। आप इस मामले में एनम सिंगलटन पैटर्न का उपयोग नहीं कर सकते हैं, क्योंकि एनम में सुपरक्लास नहीं हो सकता है (वे इंटरफेस को लागू कर सकते हैं, हालांकि)। उदाहरण के लिए, Google अमरुद एक स्थिर अंतिम क्षेत्र का उपयोग करता है, जब एनम सिंगलटन पैटर्न एक विकल्प नहीं होता है: code.google.com/p/guava-lbooks/source/browse/trunk/guava/src/…
Etienne Neutu

139

डिस्क्लेमर: मैंने सभी भयानक उत्तरों को संक्षेप में लिखा है और इसे अपने शब्दों में लिखा है।


सिंगलटन लागू करते समय हमारे पास 2 विकल्प हैं
1. आलसी लोडिंग
2. प्रारंभिक लोडिंग

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

सिंगलटन को लागू करने का सबसे सरल तरीका है

public class Foo {

    // It will be our sole hero
    private static final Foo INSTANCE = new Foo();

    private Foo() {
        if (INSTANCE != null) {
            // SHOUT
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return INSTANCE;
    }
}

इसके शुरुआती लोडेड सिंगलटन को छोड़कर सब कुछ अच्छा है। चलो आलसी लोड सिंगलटन की कोशिश करो

class Foo {

    // Our now_null_but_going_to_be sole hero 
    private static Foo INSTANCE = null;

    private Foo() {
        if (INSTANCE != null) {
            // SHOUT  
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        // Creating only  when required.
        if (INSTANCE == null) {
            INSTANCE = new Foo();
        }
        return INSTANCE;
    }
}

अब तक तो अच्छा है लेकिन हमारा हीरो कई बुरे धागों के साथ अकेले लड़ते हुए नहीं बचेगा, जो हमारे हीरो के कई उदाहरण चाहते हैं। तो इसे बुराई मल्टी थ्रेडिंग से बचाने की सुविधा देता है

class Foo {

    private static Foo INSTANCE = null;

    // TODO Add private shouting constructor

    public static Foo getInstance() {
        // No more tension of threads
        synchronized (Foo.class) {
            if (INSTANCE == null) {
                INSTANCE = new Foo();
            }
        }
        return INSTANCE;
    }
}

लेकिन यह नायक को बचाने के लिए पर्याप्त नहीं है, वास्तव में !!! यह हमारे नायक की मदद करने के लिए सबसे अच्छा हम कर सकते हैं / करना चाहिए

class Foo {

    // Pay attention to volatile
    private static volatile Foo INSTANCE = null;

    // TODO Add private shouting constructor

    public static Foo getInstance() {
        if (INSTANCE == null) { // Check 1
            synchronized (Foo.class) {
                if (INSTANCE == null) { // Check 2
                    INSTANCE = new Foo();
                }
            }
        }
        return INSTANCE;
    }
}

इसे "डबल-चेक्ड लॉकिंग मुहावरा" कहा जाता है। वाष्पशील कथन को भूलना आसान है और यह समझना मुश्किल है कि यह क्यों आवश्यक है।
जानकारी के लिए: http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

अब हम बुराई के धागे के बारे में निश्चित हैं लेकिन क्रूर क्रम के बारे में क्या? हमें यह सुनिश्चित करना होगा कि डे-सीरियलियास्टियन कोई नई वस्तु न बने

class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    private static volatile Foo INSTANCE = null;

    // Rest of the things are same as above

    // No more fear of serialization
    @SuppressWarnings("unused")
    private Object readResolve() {
        return INSTANCE;
    }
}

विधि readResolve()यह सुनिश्चित करेगी कि हमारे प्रोग्राम के पिछले भाग में ऑब्जेक्ट को सीरियल किए जाने पर भी एकमात्र उदाहरण वापस कर दिया जाएगा।

अंत में हमने थ्रेड्स और सीरियललाइज़ेशन के खिलाफ पर्याप्त सुरक्षा जोड़ी है लेकिन हमारा कोड भारी और बदसूरत लग रहा है। हमारे हीरो को एक ओवर देने की सुविधा देता है

public final class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    // Wrapped in a inner static class so that loaded only when required
    private static class FooLoader {

        // And no more fear of threads
        private static final Foo INSTANCE = new Foo();
    }

    // TODO add private shouting construcor

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }

    // Damn you serialization
    @SuppressWarnings("unused")
    private Foo readResolve() {
        return FooLoader.INSTANCE;
    }
}

हाँ यह हमारा वही नायक है :)
चूँकि लाइन private static final Foo INSTANCE = new Foo();को केवल तब क्रियान्वित FooLoaderकिया जाता है जब कक्षा वास्तव में उपयोग की जाती है, यह आलसी तात्कालिकता का ख्याल रखता है,

और क्या यह धागा सुरक्षित होने की गारंटी है।

और हम अब तक आए हैं, यहां हमने जो कुछ भी किया है उसे प्राप्त करने का सबसे अच्छा तरीका है

 public enum Foo {
       INSTANCE;
   }

जिसे आंतरिक रूप से पसंद किया जाएगा

public class Foo {

    // It will be our sole hero
    private static final Foo INSTANCE = new Foo();
}

बस! क्रमबद्धता, धागे और बदसूरत कोड का कोई और डर नहीं। इसके अलावा ENUMS सिंगलटन को आलसी रूप से आरंभीकृत किया जाता है

यह दृष्टिकोण कार्यात्मक रूप से सार्वजनिक क्षेत्र के दृष्टिकोण के बराबर है, सिवाय इसके कि यह अधिक संक्षिप्त है, मुफ्त के लिए क्रमबद्धता मशीनरी प्रदान करता है, और परिष्कृत क्रमांकन या प्रतिबिंब हमलों के सामने भी, कई तात्कालिकता के खिलाफ एक विडंबना गारंटी प्रदान करता है। हालांकि इस दृष्टिकोण को व्यापक रूप से अपनाया जाना बाकी है, सिंगल-एलिमेंट एनम टाइप सिंगलटन को लागू करने का सबसे अच्छा तरीका है।

-जोशुआ बलोच "प्रभावी जावा" में

अब आप समझ गए होंगे कि क्यों ENUMS को सिंगलटन को लागू करने का सबसे अच्छा तरीका माना जाता है और आपके धैर्य के लिए धन्यवाद :)
मेरे ब्लॉग पर इसे अपडेट करें


3
बस एक स्पष्टीकरण: एनम का उपयोग करके कार्यान्वित सिंगलेट्स को प्रारंभिक रूप से आलसी बनाया जाता है। यहाँ विवरण: stackoverflow.com/questions/16771373/…

2
बहुत बढ़िया जवाब। एक आखिरी बात, अपवाद को फेंकने के लिए क्लोन विधि को ओवरराइड करें।
ऋषिप89

2
@xyz अच्छा स्पष्टीकरण, मुझे वास्तव में बहुत मज़ा आया और आसानी से सीखा और मुझे आशा है कि यह कभी नहीं भूली होगी
प्रेमराज

1
सबसे अच्छे जवाबों में से एक है जिसका मैंने कभी स्टैकओवरफ्लो पर लाल किया है। धन्यवाद!
शाए त्सादोक

1
वहाँ है एक सिंगलटन के रूप में enums का उपयोग कर के साथ एक क्रमबद्धता समस्या: कोई भी सदस्य फ़ील्ड मान रहे हैं नहीं धारावाहिक और इसलिए पुनर्स्थापित नहीं किया। जावा ऑब्जेक्ट सीरियलाइज़ेशन विनिर्देश देखें , संस्करण 6.0 । एक और समस्या: कोई संस्करण नहीं - सभी ईनम प्रकारों का एक निश्चित serialVersionUIDहै 0L। तीसरी समस्या: कोई अनुकूलन नहीं: कोई भी वर्ग-विशेष लिखने वाला विशेषण, readObject, readObjectNoData, writeReplace, और enRes प्रकारों द्वारा परिभाषित readResolve तरीकों को क्रमबद्धता और deserialization के दौरान अनदेखा किया जाता है।
बेसिल बोर्के

124

स्टु थॉम्पसन द्वारा पोस्ट किया गया समाधान Java5.0 और बाद में मान्य है। लेकिन मैं इसे उपयोग नहीं करना पसंद करूंगा क्योंकि मुझे लगता है कि यह त्रुटि प्रवण है।

वाष्पशील कथन को भूलना आसान है और यह समझना मुश्किल है कि यह क्यों आवश्यक है। अस्थिरता के बिना यह कोड डबल-चेक लॉकिंग एंटीपैटर्न के कारण थ्रेड सुरक्षित नहीं होगा। इसके बारे में अधिक देखें प्रैक्टिस में जावा कॉनएरेबिलिटी के पैरा 16.2.4 में । संक्षेप में: यह पैटर्न (Java5.0 से पहले या अस्थिर बयान के बिना) एक गलत स्थिति में बार ऑब्जेक्ट (जो अभी भी है) का संदर्भ दे सकता है।

यह पैटर्न प्रदर्शन अनुकूलन के लिए आविष्कार किया गया था। लेकिन यह वास्तव में अब कोई वास्तविक चिंता नहीं है। निम्नलिखित आलसी इनिशियलाइज़ेशन कोड तेज़ और -मोर महत्वपूर्ण है- पढ़ने में आसान।

class Bar {
    private static class BarHolder {
        public static Bar bar = new Bar();
    }

    public static Bar getBar() {
        return BarHolder.bar;
    }
}

1
काफी उचित! मैं सिर्फ वाष्पशील के साथ सहज हूं और इसका उपयोग कर रहा हूं। ओह, और JCiP के लिए तीन चीयर्स।
स्ट्यू थॉम्पसन

1
ओह, यह जाहिरा तौर पर FindBugz प्रसिद्धि के विलियम Pugh द्वारा वकालत दृष्टिकोण है।
स्टु थॉम्पसन

4
@Stu प्रभावी जावा (कॉपीराइट 2001) के पहले संस्करण में आइटम 48 के तहत इस पैटर्न का विवरण है।
पास्कल थिवेंट

9
@ बानो: कंस्ट्रक्टर को निजी बनाने के बारे में क्या?
xyz

2
@ AlikElzin-kilaka बिलकुल नहीं। उदाहरण BarHolder के लिए वर्ग लोडिंग चरण में बनाया गया है , जो पहली बार इसकी आवश्यकता होने तक विलंबित है। बार का निर्माता जितना चाहे उतना जटिल हो सकता है, लेकिन यह पहले तक बुलाया नहीं जाएगा getBar()। (और अगर getBarइसे "बहुत जल्दी" कहा जाता है, तो आपको एक ही समस्या का सामना करना पड़ेगा चाहे कोई एकल कैसे लागू हो।) आप ऊपर दिए गए कोड के आलसी वर्ग को यहां देख सकते हैं: pastebin.com/iq2eayiR
Ti Strga

95

जावा 5+ में सुरक्षित धागा:

class Foo {
    private static volatile Bar bar = null;
    public static Bar getBar() {
        if (bar == null) {
            synchronized(Foo.class) {
                if (bar == null)
                    bar = new Bar(); 
            }
        }
        return bar;
    }
}

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

EDIT 2 : @Bno का उत्तर विधेयक पुघ (FindBugs) द्वारा सुझाए गए दृष्टिकोण का विवरण है और यह बेहतर है। पढ़ो और उसके जवाब को भी वोट करो।


1
मैं अस्थिर संशोधक के बारे में और अधिक कहाँ जान सकता हूँ?
ग्यारह ग्यारह

की टिप्पणी देखें stackoverflow.com/questions/70689/...
पास्कल Thivent

2
मुझे लगता है कि प्रतिबिंब हमलों के बारे में उल्लेख करना महत्वपूर्ण है। यह सच है कि अधिकांश डेवलपर्स को इसके बारे में चिंता करने की ज़रूरत नहीं है, लेकिन ऐसा लगता है कि इन (एनम-आधारित सिंगलटन पर) जैसे उदाहरणों में या तो कोड शामिल होना चाहिए जो कई-तात्कालिक हमलों से बचाता है या ऐसी संभावनाओं का संकेत देने वाला अस्वीकरण डाल सकता है।
luis.espinal

2
यहां अस्थिर कीवर्ड की आवश्यकता नहीं है - क्योंकि सिंक्रोनाइज़ेशन दोनों को पारस्परिक बहिष्करण और मेमोरी विजिबिलिटी देता है।
हेमंत

2
जावा 5+ में यह सब क्यों परेशान करता है? मेरी समझ यह है कि एनम दृष्टिकोण थ्रेड सुरक्षा और आलसी प्रारंभ दोनों प्रदान करता है। यह बहुत सरल है ... इसके अलावा, यदि आप एक एनम से बचना चाहते हैं, तो मैं अभी भी नेस्टेड स्टैटिक क्लास दृष्टिकोण को
एलेक्जेंड्रोस

91

आलसी आरंभीकरण को भूल जाओ , यह बहुत समस्याग्रस्त है। यह सबसे सरल उपाय है:

public class A {    

    private static final A INSTANCE = new A();

    private A() {}

    public static A getInstance() {
        return INSTANCE;
    }
}

21
एकल उदाहरण चर को भी अंतिम रूप दिया जा सकता है। उदाहरण के लिए, निजी स्थिर फाइनल ए सिंगलटन = नया ए ();
जाटान सिप

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

7
यदि क्लास ए को लोडेड तरीका मिलता है, इससे पहले कि आप चाहते हैं कि स्टेटिक को तुरंत हटा दिया जाए, तो आप क्लास की इनिशियलाइज़ेशन को कम करने के लिए स्टेटिक इनर क्लास में स्टेटिक को लपेट सकते हैं।
टॉम हॉकिन - 12

3
मैं मानता हूं कि यह उत्तर सबसे सरल है, और अनिरुद्धन, उदाहरण की अंतिम घोषणा करने की कोई आवश्यकता नहीं है। किसी अन्य थ्रेड को क्लास तक पहुँच प्राप्त नहीं होगी, जबकि स्थैतिक सदस्यों को आरंभीकृत किया जाता है। यह कंपाइलर द्वारा गारंटीकृत है, दूसरे शब्दों में, सभी स्थिर इनिशियलाइज़ेशन को एक सिंक्रनाइज़ तरीके से किया जाता है - केवल एक धागा।
inor

4
इस दृष्टिकोण में एक सीमा है: कंस्ट्रक्टर अपवाद नहीं फेंक सकता है।
wangf

47

सुनिश्चित करें कि आपको वास्तव में इसकी आवश्यकता है। इसके खिलाफ कुछ तर्क देखने के लिए "सिंगलटन-पैटर्न" के लिए एक Google करें। इसमें कुछ भी गलत नहीं है क्योंकि मुझे लगता है लेकिन यह सिर्फ कुछ वैश्विक संसाधन / डेटा को उजागर करने के लिए एक तंत्र है इसलिए सुनिश्चित करें कि यह सबसे अच्छा तरीका है। विशेष रूप से मैंने निर्भरता इंजेक्शन को अधिक उपयोगी पाया है, खासकर यदि आप यूनिट परीक्षणों का उपयोग कर रहे हैं क्योंकि DI आपको परीक्षण उद्देश्यों के लिए नकली संसाधनों का उपयोग करने की अनुमति देता है।


आप पारंपरिक विधि के साथ नकली मानों को भी इंजेक्ट कर सकते हैं, लेकिन मुझे लगता है कि इसका मानक / क्षेपण तरीका नहीं है, इसलिए इसका अतिरिक्त काम केवल विरासत कोड प्राप्त करने के साथ है ...
tgkprog

21

मैं कुछ ऐसे जवाबों से विभूषित हूं, जो DI को सिंगलटन के उपयोग के विकल्प के रूप में सुझाते हैं; ये असंबंधित अवधारणाएँ हैं। आप किसी भी सिंगलटन या गैर-सिंगलटन (जैसे प्रति-धागा) उदाहरणों को इंजेक्ट करने के लिए DI का उपयोग कर सकते हैं। यदि आप स्प्रिंग 2.x का उपयोग करते हैं तो कम से कम यह सच है, मैं अन्य DI फ्रेमवर्क के लिए बात नहीं कर सकता।

तो ओपी के लिए मेरा जवाब होगा (सभी में सबसे तुच्छ नमूना कोड):

  1. स्प्रिंग की तरह DI फ्रेम का प्रयोग करें
  2. इसे अपने DI कॉन्फ़िगरेशन का हिस्सा बनाएं, चाहे आपकी निर्भरता एकल हो, अनुरोधित स्कूप, सत्र स्कोप, या जो भी हो।

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


4
शायद इसलिए कि लोग आपसे असहमत हैं। मैंने आपको निराश नहीं किया है, लेकिन मैं असहमत हूं: मुझे लगता है कि डीआई का उपयोग उसी समस्याओं को हल करने के लिए किया जा सकता है जो एकल हैं। यह "सिंगलटन" को एक एकल उदाहरण के साथ एक वस्तु के रूप में समझने के लिए है, जिसका अर्थ है "वैश्विक उदाहरण के द्वारा सीधे एक्सेस किया जाता है", बजाय केवल "एक एकल उदाहरण के साथ एक वस्तु", जो शायद थोड़ा मुश्किल है।
टॉम एंडरसन

2
उस पर थोड़ा विस्तार करने के लिए, TicketNumbererजिस पर एक वैश्विक उदाहरण होना चाहिए, और जहां आप एक वर्ग लिखना चाहते हैं, TicketIssuerजिसमें कोड की एक पंक्ति है, पर विचार करें int ticketNumber = ticketNumberer.nextTicketNumber();। पारंपरिक सिंगलटन सोच में, कोड की पिछली पंक्ति कुछ इस तरह की होगी TicketNumberer ticketNumberer = TicketNumberer.INSTANCE;। डीआई सोच में, कक्षा में एक निर्माता होगा public TicketIssuer(TicketNumberer ticketNumberer) { this.ticketNumberer = ticketNumberer; }
टॉम एंडरसन

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

@TomAnderson मैं वास्तव में उलझन में हूं कि लोगों ने सर्विस लोकेटर पैटर्न को 'ड्रेड' क्यों किया। मुझे लगता है कि ज्यादातर मामलों में यह ओवरकिल है या सबसे अच्छी जरूरत नहीं है, हालांकि, प्रतीत होता है कि उपयोगी मामले हैं। Params की छोटी संख्या के साथ DI निश्चित रूप से पसंद किया जाता है, लेकिन कल्पना 20+। यह कहते हुए कि कोड संरचित नहीं है, एक मान्य तर्क नहीं है, क्योंकि कभी-कभी मापदंडों का समूह केवल मतलब नहीं रखता है। इसके अलावा, एक इकाई परीक्षण के नजरिए से, मैं इस सेवा के परीक्षण के बारे में परवाह नहीं करता, बस इसके व्यापारिक तर्क, और अगर इसे सही कोडित किया जाता है, तो यह आसान होगा। मैंने केवल इस आवश्यकता को बहुत बड़े पैमाने पर परियोजनाओं में देखा।
ब्रैंडन लिंग

20

वास्तव में विचार करें कि इसे लिखने से पहले आपको एक सिंगलटन की आवश्यकता क्यों है। उनका उपयोग करने के बारे में एक अर्ध-धार्मिक बहस है जिसे आप जावा में Google सिंगललेट्स पर आसानी से रोक सकते हैं।

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

यदि आपको वास्तव में एक की आवश्यकता है तो विकिपीडिया एक सिंगलटन के उचित कार्यान्वयन का एक अच्छा उदाहरण है।


माना। यह एक मूलभूत वर्ग है जो आपके बाकी एप्लिकेशन को किक-स्टार्ट करता है और यदि इसे डुप्लिकेट किया गया तो आप एक पूर्ण अराजकता (यानी एक संसाधन या सुरक्षा को लागू करना) के साथ समाप्त हो जाएंगे। आपके एप्लिकेशन पर वैश्विक डेटा पास करना एक बड़ा युग्मन लाल झंडा है। इसका उपयोग तब करें जब आप स्वीकार करते हैं कि आपको वास्तव में इसकी आवश्यकता है।
साल्वाडोर वालेंसिया

16

निम्नलिखित 3 अलग-अलग दृष्टिकोण हैं

1) एनम

/**
* Singleton pattern example using Java Enumj
*/
public enum EasySingleton{
    INSTANCE;
}

2) डबल जाँच ताला / आलसी लोड हो रहा है

/**
* Singleton pattern example with Double checked Locking
*/
public class DoubleCheckedLockingSingleton{
     private static volatile DoubleCheckedLockingSingleton INSTANCE;

     private DoubleCheckedLockingSingleton(){}

     public static DoubleCheckedLockingSingleton getInstance(){
         if(INSTANCE == null){
            synchronized(DoubleCheckedLockingSingleton.class){
                //double checking Singleton instance
                if(INSTANCE == null){
                    INSTANCE = new DoubleCheckedLockingSingleton();
                }
            }
         }
         return INSTANCE;
     }
}

3) स्थिर कारखाने विधि

/**
* Singleton pattern example with static factory method
*/

public class Singleton{
    //initailzed during class loading
    private static final Singleton INSTANCE = new Singleton();

    //to prevent creating another instance of Singleton
    private Singleton(){}

    public static Singleton getSingleton(){
        return INSTANCE;
    }
}

13

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


11

विकिपीडिया में एकल के कुछ उदाहरण हैं, जावा में भी। जावा 5 कार्यान्वयन बहुत पूर्ण दिखता है, और थ्रेड-सेफ (डबल-चेक लॉकिंग एप्लाइड) है।


11

संस्करण 1:

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

आलसी लोडिंग, ब्लॉकिंग के साथ थ्रेड सुरक्षित, कम प्रदर्शन के कारण synchronized

संस्करण 2:

public class MySingleton {
    private MySingleton() {}
    private static class MySingletonHolder {
        public final static MySingleton instance = new MySingleton();
    }
    public static MySingleton getInstance() {
        return MySingletonHolder.instance;
    }
}

आलसी लोडिंग, गैर-अवरोधक, उच्च प्रदर्शन के साथ थ्रेड सुरक्षित।


10

यदि आपको आलसी लोडिंग की आवश्यकता नहीं है, तो बस कोशिश करें

public class Singleton {
    private final static Singleton INSTANCE = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() { return Singleton.INSTANCE; }

    protected Object clone() {
        throw new CloneNotSupportedException();
    }
}

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

public class Singleton {
        private static Singleton instance = null;

        private Singleton() {}

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

        protected Object clone() {
            throw new CloneNotSupportedException();
        }
}

जैसा कि डबल चेकिंग पैटर्न को काम करने की गारंटी नहीं है (कंपाइलर्स के साथ कुछ मुद्दे के कारण, मुझे इसके बारे में अधिक कुछ भी नहीं पता है।), आप पूरे getInstance-method को सिंक्रनाइज़ करने या अपने सभी सिंगलेट्स के लिए एक रजिस्ट्री बनाने का भी प्रयास कर सकते हैं।


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

1
एक स्थिर के लिए डबल-चेकिंग व्यर्थ है। और आपने संरक्षित क्लोन पद्धति को सार्वजनिक क्यों किया है?
टॉम हॉकिन -

1
-1 आपके डबल चेक्ड लॉकिंग का संस्करण टूट गया है।
assylias

5
इसके अलावा आपको अपना सिंगलटन वैरिएबल बनाने की जरूरत हैvolatile
MyTitle

पहला संस्करण है आलसी और धागे की सुरक्षित।
Miha_x64

9

मैं Enum सिंगलटन कहूँगा

जावा में एनम का उपयोग करने वाला सिंगलटन आमतौर पर एनम सिंगलटन घोषित करने का तरीका है। Enum singleton में आवृत्ति चर और आवृत्ति विधि हो सकती है। सादगी की खातिर, यह भी ध्यान दें कि यदि आप किसी भी उदाहरण विधि का उपयोग कर रहे हैं तो आपको उस विधि की थ्रेड सुरक्षा सुनिश्चित करने की आवश्यकता है यदि यह सभी वस्तु की स्थिति को प्रभावित करता है।

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

/**
* Singleton pattern example using Java Enum
*/
public enum Singleton {
        INSTANCE;
        public void execute (String arg) {
                //perform operation here
        }
}

आप सिंगलटन पर Singleton.INSTANCEकॉलिंग getInstance()विधि की तुलना में इसे बहुत आसान बना सकते हैं ।

1.12 Enum Constants का Serialization

Enum constants को साधारण serializable या बाहरी वस्तुओं की तुलना में अलग तरीके से क्रमबद्ध किया जाता है। एक एनम स्थिरांक के क्रमबद्ध रूप में केवल इसका नाम होता है; स्थिरांक के क्षेत्र मान रूप में मौजूद नहीं हैं। एक एनम स्थिरांक को क्रमबद्ध करने के लिए, ObjectOutputStreamएनम निरंतर के नाम विधि द्वारा लौटाया गया मान लिखते हैं। एक एनम स्थिरांक को डिस्क्राइब करने के लिए, ObjectInputStreamस्ट्रीम से निरंतर नाम पढ़ता है; deserialized स्थिरांक तब java.lang.Enum.valueOfविधि को कॉल करके प्राप्त किया जाता है , निरंतर स्थिर नाम को तर्क के रूप में प्राप्त निरंतर नाम के साथ गुजरता है। अन्य अनुक्रमिक या बाहरी वस्तुओं की तरह, एनरम स्थिरांक क्रमिक धारा में बाद में दिखाई देने वाले बैक रेफरेंस के लक्ष्य के रूप में कार्य कर सकते हैं।

प्रक्रिया है जिसके द्वारा enum स्थिरांक क्रमानुसार लगे हुए हैं अनुकूलित नहीं किया जा सकता: किसी भी वर्ग-विशेष writeObject, readObject, readObjectNoData, writeReplace, और readResolveतरीकों enum प्रकार से परिभाषित किया गया क्रमबद्धता और अक्रमांकन दौरान अनदेखी कर रहे हैं। इसी तरह, किसी भी serialPersistentFieldsया serialVersionUIDक्षेत्र की घोषणाओं को भी नजरअंदाज कर दिया जाता है - सभी enum प्रकारों का एक निश्चित serialVersionUIDहोता है 0L। सीरम प्रकारों के लिए दस्तावेजीकरण फ़ील्ड और डेटा का दस्तावेज़ अनावश्यक है, क्योंकि भेजे गए डेटा के प्रकार में कोई भिन्नता नहीं है।

Oracle डॉक्स से उद्धृत

पारंपरिक सिंग्लेटन्स के साथ एक और समस्या यह है कि एक बार जब आप Serializableइंटरफ़ेस लागू करते हैं , तो वे अब सिंगलटन नहीं रह जाते हैं क्योंकि readObject()विधि हमेशा जावा में कंस्ट्रक्टर की तरह एक नया उदाहरण लौटाती है। readResolve()नीचे दिए गए सिंगलटन के साथ बदलकर नए बनाए गए उदाहरण का उपयोग करके और इसे त्यागने से बचा जा सकता है

 // readResolve to prevent another instance of Singleton
 private Object readResolve(){
     return INSTANCE;
 }

यह और भी जटिल हो सकता है यदि आपका सिंगलटन क्लास राज्य बनाए रखता है, जैसा कि आपको उन्हें क्षणिक बनाने की आवश्यकता है, लेकिन एनम सिंगलटन के साथ, सीरियल को जेवीएम द्वारा गारंटी दी जाती है।


अच्छा पढ़ा

  1. सिंगलटन पैटर्न
  2. एनम, सिंगलेट्स और डिसेरिएलाइज़ेशन
  3. डबल-चेकिंग लॉकिंग और सिंगलटन पैटर्न

8
There are 4 ways to create a singleton in java.

1- eager initialization singleton

    public class Test{
        private static final Test test = new Test();
        private Test(){}
        public static Test getTest(){
            return test;
        }
    

2- lazy initialization singleton (thread safe)

    public class Test {
         private static volatile Test test;
         private Test(){}
         public static Test getTest() {
            if(test == null) {
                synchronized(Test.class) {
                    if(test == null){test = new Test();
                }
            }
         }

        return test;
    


3- Bill Pugh Singleton with Holder Pattern (Preferably the best one)

    public class Test {

        private Test(){}

        private static class TestHolder{
            private static final Test test = new Test();
        }

        public static Test getInstance(){
            return TestHolder.test;
        }
    }

4- enum singleton
      public enum MySingleton {
        INSTANCE;
    private MySingleton() {
        System.out.println("Here");
    }
}

(1) उत्सुक नहीं है, यह जेवीएम वर्ग लोडिंग तंत्र के कारण आलसी है।
मिहा_एक्स 64

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

प्रभावी जावा एक महान पुस्तक है, लेकिन निश्चित रूप से संपादन की आवश्यकता है।
मिह_एक्स ६४

@ Miha_x64 क्या उत्सुक है लोड हो रहा है, क्या आप उदाहरण द्वारा समझा सकते हैं
धीरज सचान

Do उत्सुकता ’से कुछ करने का अर्थ है ly जितनी जल्दी हो सके’। उदाहरण के लिए, हाइबरनेट स्पष्ट रूप से आवश्यक होने पर, लोडिंग संबंधों का उत्सुकता से समर्थन करता है।
Miha_x64

8

इस पर खेल में थोड़ी देर हो सकती है, लेकिन एक सिंगलटन को लागू करने के आसपास बहुत सारी बारीकियां हैं। धारक पैटर्न का उपयोग कई स्थितियों में नहीं किया जा सकता है। और वाष्पशील का उपयोग करते समय IMO - आपको एक स्थानीय चर का उपयोग करना चाहिए। आइए शुरुआत में शुरू करते हैं और समस्या पर पुनरावृति करते हैं। आप देखेंगे कि मेरा क्या मतलब है।


पहला प्रयास कुछ इस तरह दिख सकता है:

public class MySingleton {

     private static MySingleton INSTANCE;

     public static MySingleton getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new MySingleton();
        }

        return INSTANCE;
    }
    ...
}

यहां हमारे पास MySingleton वर्ग है जिसमें एक निजी स्थिर सदस्य है जिसे INSTANCE कहा जाता है, और एक सार्वजनिक स्थैतिक विधि जिसे getInstance () कहा जाता है। पहली बार getInstance () कहा जाता है, INSTANCE सदस्य शून्य है। तब प्रवाह निर्माण की स्थिति में आ जाएगा और MySingleton वर्ग का एक नया उदाहरण तैयार करेगा। इसके बाद कॉल करने के लिए getInstance () यह पाएगा कि INSTANCE वैरिएबल पहले से सेट है, और इसलिए एक और MySletonleton उदाहरण न बनाएं। यह सुनिश्चित करता है कि MySingleton का केवल एक उदाहरण है जो getInstance () के सभी कॉलर्स के बीच साझा किया गया है।

लेकिन इस कार्यान्वयन में एक समस्या है। बहु-थ्रेडेड अनुप्रयोगों में एकल उदाहरण के निर्माण पर एक दौड़ की स्थिति होगी। यदि निष्पादन के कई थ्रेड्स एक ही समय में getInstance () विधि (या उसके आसपास) से टकराते हैं, तो वे प्रत्येक INSTANCE सदस्य को अशक्त के रूप में देखेंगे। इसके परिणामस्वरूप प्रत्येक थ्रेड एक नया MySingleton उदाहरण बनाता है और बाद में INSTANCE सदस्य को सेट करता है।


private static MySingleton INSTANCE;

public static synchronized MySingleton getInstance() {
    if (INSTANCE == null) {
        INSTANCE = new MySingleton();
    }

    return INSTANCE;
}

यहां हमने getInstance () विधि को सिंक्रनाइज़ करने के लिए विधि हस्ताक्षर में सिंक्रनाइज़ कीवर्ड का उपयोग किया है। यह निश्चित रूप से हमारी दौड़ की स्थिति को ठीक करेगा। थ्रेड्स अब ब्लॉक हो जाएंगे और एक समय में विधि एक में प्रवेश करेंगे। लेकिन यह एक प्रदर्शन समस्या भी पैदा करता है। न केवल यह कार्यान्वयन एकल उदाहरण के निर्माण को सिंक्रनाइज़ करता है, यह रीड्स सहित, getInstance () के लिए सभी कॉल को सिंक्रनाइज़ करता है। रीड्स को सिंक्रनाइज़ करने की आवश्यकता नहीं है क्योंकि वे बस INSTANCE का मूल्य वापस करते हैं। चूँकि रीड हमारे कॉल का बड़ा हिस्सा बन जाएगा (याद रखें, तात्कालिकता केवल पहली कॉल पर होती है), हम पूरी विधि को सिंक्रनाइज़ करके एक अनावश्यक प्रदर्शन को प्रभावित करेंगे।


private static MySingleton INSTANCE;

public static MySingleton getInstance() {
    if (INSTANCE == null) {
        synchronize(MySingleton.class) {
            INSTANCE = new MySingleton();
        }
    }

    return INSTANCE;
}

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


private static MySingleton INSTANCE;

public static MySingleton getInstance() {
    if (INSTANCE == null) {
        synchronized(MySingleton.class) {
            if (INSTANCE == null) {
                INSTANCE = createInstance();
            }
        }
    }

    return INSTANCE;
}

यहां हम INSIDE ब्लॉक से एक और चेक जारी करते हैं। यदि INSTANCE सदस्य पहले ही सेट कर दिया गया है, तो हम प्रारंभ को छोड़ देंगे। इसे डबल-चेकिंग लॉकिंग कहा जाता है।

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


private static volatile MySingleton INSTANCE;

public static MySingleton getInstance() {
    if (INSTANCE == null) {
        synchronized(MySingleton.class) {
            if (INSTANCE == null) {
                INSTANCE = createInstance();
            }
        }
    }

    return INSTANCE;
}

हम इसे INSTANCE सदस्य की घोषणा पर अस्थिर कीवर्ड का उपयोग करके हल करते हैं। यह संकलक को हमेशा पढ़ने, और मुख्य मेमोरी, और सीपीयू कैश से लिखने के लिए बताएगा।

लेकिन यह साधारण बदलाव एक कीमत पर आता है। क्योंकि हम सीपीयू कैश को दरकिनार कर रहे हैं, हम प्रत्येक बार जब हम अस्थिर इंस्टेंस सदस्य पर काम करते हैं, तो एक प्रदर्शन करेंगे - जो हम 4 बार करते हैं। हम अस्तित्व (1 और 2) को दोहराते हैं, मान (3) सेट करते हैं, और फिर मान (4) वापस करते हैं। कोई यह तर्क दे सकता है कि यह रास्ता फ्रिंज केस है क्योंकि हम केवल विधि के पहले कॉल के दौरान ही उदाहरण बनाते हैं। शायद निर्माण पर हिट एक प्रदर्शन सहनीय है। लेकिन यहां तक ​​कि हमारा मुख्य उपयोग-मामला, वाष्पशील सदस्य पर दो बार काम करेगा। एक बार अस्तित्व की जांच करने के लिए, और फिर से इसके मूल्य को वापस करने के लिए।


private static volatile MySingleton INSTANCE;

public static MySingleton getInstance() {
    MySingleton result = INSTANCE;
    if (result == null) {
        synchronized(MySingleton.class) {
            result = INSTANCE;
            if (result == null) {
                INSTANCE = result = createInstance();
            }
        }
    }

    return result;
}

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

मैंने हाल ही में इस बारे में एक लेख लिखा था। सिंगलाटन का पुनर्निर्माण । आप इन उदाहरणों के बारे में अधिक जानकारी और वहां "धारक" पैटर्न का एक उदाहरण पा सकते हैं। डबल-चेक किए गए अस्थिर दृष्टिकोण को दर्शाने वाला एक वास्तविक-विश्व उदाहरण भी है। उम्मीद है की यह मदद करेगा।


क्या आप कृपया बता सकते हैं कि BearerToken instanceआपके लेख में ऐसा क्यों नहीं है static? और क्या है result.hasExpired()?
वोलैंड

और किस बारे में class MySingleton- शायद यह होना चाहिए final?
Woland

1
@Oland BearerTokenउदाहरण स्थिर नहीं है क्योंकि यह उसी का हिस्सा है BearerTokenFactory - जिसे एक विशिष्ट ऑथराइजेशन सर्वर से कॉन्फ़िगर किया गया है। कई BearerTokenFactoryवस्तुएं हो सकती हैं - हर एक का अपना "कैश" है BearerTokenजो इसे समाप्त होने तक सौंपता है। hasExpired()पर विधि BeraerTokenकारखाने के में कहा जाता है get()विधि सुनिश्चित करने के लिए इसे बाहर हाथ नहीं है एक टोकन की समयावधि समाप्त। यदि समय सीमा समाप्त हो जाती है, तो एक नया टोकन अनुरोध किया जाएगा। कोड ब्लॉक के बाद का पैराग्राफ इसे अधिक विस्तार से बताता है।
माइकल एंड्रयूज

4

यह एक सरल लागू करने का तरीका है singleton:

public class Singleton {
    // It must be static and final to prevent later modification
    private static final Singleton INSTANCE = new Singleton();
    /** The constructor must be private to prevent external instantiation */ 
    private Singleton(){}
    /** The public static method allowing to get the instance */
    public static Singleton getInstance() {
        return INSTANCE;
    }
}

यह है कि कैसे ठीक से आलसी अपने बनाने के लिए singleton:

public class Singleton {
    // The constructor must be private to prevent external instantiation   
    private Singleton(){}
    /** The public static method allowing to get the instance */
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
    /** 
     * The static inner class responsible for creating your instance only on demand,
     * because the static fields of a class are only initialized when the class
     * is explicitly called and a class initialization is synchronized such that only 
     * one thread can perform it, this rule is also applicable to inner static class
     * So here INSTANCE will be created only when SingletonHolder.INSTANCE 
     * will be called
     */
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
}

दोनों आलसी हैं, एकमात्र चीज जिसे आप सिंगलटन से चाहते हैं, वह इसका उदाहरण है।
Miha_x64

@ Miha_x64 पहला मामला सिंगलटन को इंस्टेंट कर देगा जब जेवीएम क्लास को इनिशियलाइज़ करता है, दूसरा केस कॉल करते समय केवल सिंगलटन को इंस्टेंट करेगा getInstance()। लेकिन वास्तव में अगर आपके पास अपनी कक्षा में कोई अन्य स्थिर पद्धति नहीं है Singletonऔर आप केवल कॉल करते हैं getInstance()तो कोई वास्तविक अंतर नहीं है।
निकोलस फिलोटो

3

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

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

public static final INSTANCE == ....
private Object readResolve() {
  return INSTANCE; // original singleton instance.
} 

3

सिंगलटन ऑब्जेक्ट बनाने के विभिन्न तरीके:

  1. यहोशू बलोच के अनुसार - एनम सबसे अच्छा होगा।

  2. आप डबल चेक लॉकिंग का भी उपयोग कर सकते हैं।

  3. यहां तक ​​कि आंतरिक स्थिर वर्ग का उपयोग किया जा सकता है।


3

एनम सिंगलटन

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

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

JSE 5.0 और इसके बाद के संस्करण के लिए Enum अप्रोच लें, अन्यथा स्टेटिक सिंगलटन होल्डर एप्रोच का उपयोग करें ((एक हल्का लोडिंग अप्रोच जो कि Pugh द्वारा वर्णित है)। लैटर सॉल्यूशन भी विशेष भाषा निर्माण (यानी वाष्पशील या सिंक्रोनाइज़) की आवश्यकता के बिना थ्रेड-सेफ है।


2

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

public class SingletonImpl {

    private static SingletonImpl instance;

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

    public static void setInstance(SingletonImpl impl) {
        instance = impl;
    }

    public void a() {
        System.out.println("Default Method");
    }
}

जोड़ा setInstanceविधि परीक्षण के दौरान सिंगलटन वर्ग के एक mockup कार्यान्वयन की स्थापना की अनुमति देता है:

public class SingletonMock extends SingletonImpl {

    @Override
    public void a() {
        System.out.println("Mock Method");
    }

}

यह प्रारंभिक आरंभीकरण दृष्टिकोण के साथ भी काम करता है:

public class SingletonImpl {

    private static final SingletonImpl instance = new SingletonImpl();

    private static SingletonImpl alt;

    public static void setInstance(SingletonImpl inst) {
        alt = inst;
    }

    public static SingletonImpl getInstance() {
        if (alt != null) {
            return alt;
        }
        return instance;
    }

    public void a() {
        System.out.println("Default Method");
    }
}

public class SingletonMock extends SingletonImpl {

    @Override
    public void a() {
        System.out.println("Mock Method");
    }

}

यह इस कार्यक्षमता को सामान्य अनुप्रयोग के लिए भी उजागर करने का दोष है। उस कोड पर काम करने वाले अन्य डेवलपर्स को एक विशिष्ट फ़ंक्शन को बदलने और इस प्रकार पूरे एप्लिकेशन व्यवहार को बदलने के लिए codesetInstance to विधि का उपयोग करने के लिए लुभाया जा सकता है, इसलिए इस विधि में कम से कम एक अच्छी चेतावनी इसमें शामिल होनी चाहिए।

फिर भी, मॉकअप-टेस्टिंग (जब जरूरत हो) की संभावना के लिए, इस कोड एक्सपोज़र को भुगतान करने के लिए स्वीकार्य मूल्य हो सकता है।


1

सबसे सरल एकल वर्ग

public class Singleton {
  private static Singleton singleInstance = new Singleton();
  private Singleton() {}
  public static Singleton getSingleInstance() {
    return singleInstance;
  }
}

1
यह नीचे जोनाथन के जवाब के समान है
inor

1
का डुप्लीकेट इस भाई जवाब से जोनाथन पांच साल पहले था। दिलचस्प टिप्पणियों के लिए वह जवाब देखें।
बेसिल बोर्के

0

मुझे अभी भी लगता है कि java 1.5 के बाद, enum सबसे अच्छा उपलब्ध सिंगलटन कार्यान्वयन है क्योंकि यह यह भी सुनिश्चित करता है कि बहु थ्रेडेड वातावरण में भी - केवल एक ही उदाहरण बनता है।

public enum Singleton{ INSTANCE; }

और आप कर रहे हैं !!!


1
यह पहले से ही अन्य उत्तरों में वर्षों पहले उल्लेख किया गया है।
पैंग

0

इस पोस्ट पर एक नजर।

जावा के मुख्य पुस्तकालयों में जीओएफ डिज़ाइन पैटर्न के उदाहरण

सर्वश्रेष्ठ उत्तर के "सिंगलटन" अनुभाग से,

सिंगलटन (समान रूप से स्वयं को लौटाने वाली रचनात्मक विधियों द्वारा पहचाने जाने योग्य)

  • java.lang.Runtime # getRuntime ()
  • java.awt.Desktop # getDesktop ()
  • java.lang.System # getSecurityManager ()

आप स्वयं जावा मूल वर्गों से सिंग्लटन का उदाहरण भी सीख सकते हैं।


0

मैंने कभी देखा है सबसे अच्छा सिंगलटन पैटर्न आपूर्तिकर्ता इंटरफ़ेस का उपयोग करता है।

  • यह सामान्य और पुन: प्रयोज्य है
  • यह आलसी आरंभीकरण का समर्थन करता है
  • इसे केवल तब तक सिंक्रोनाइज़ किया जाता है जब तक इसे इनिशियलाइज़ नहीं किया जाता है, तब ब्लॉकिंग सप्लायर को नॉन-ब्लॉकिंग सप्लायर से बदल दिया जाता है।

निचे देखो:

public class Singleton<T> implements Supplier<T> {

    private boolean initialized;
    private Supplier<T> singletonSupplier;

    public Singleton(T singletonValue) {
        this.singletonSupplier = () -> singletonValue;
    }

    public Singleton(Supplier<T> supplier) {
        this.singletonSupplier = () -> {
            // The initial supplier is temporary; it will be replaced after initialization
            synchronized (supplier) {
                if (!initialized) {
                    T singletonValue = supplier.get();
                    // Now that the singleton value has been initialized,
                    // replace the blocking supplier with a non-blocking supplier
                    singletonSupplier = () -> singletonValue;
                    initialized = true;
                }
                return singletonSupplier.get();
            }
        };
    }

    @Override
    public T get() {
        return singletonSupplier.get();
    }
}

-3

कभी-कभी एक सरल " static Foo foo = new Foo();" पर्याप्त नहीं होता है। बस कुछ बुनियादी डेटा प्रविष्टि के बारे में सोचें जो आप करना चाहते हैं।

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

public class Singleton {

    private static Singleton instance = null;

    static {
          instance = new Singleton();
          // do some of your instantiation stuff here
    }

    private Singleton() {
          if(instance!=null) {
                  throw new ErrorYouWant("Singleton double-instantiation, should never happen!");
          }
    }

    public static getSingleton() {
          return instance;
    }

}

अब क्या होता है? वर्ग को लोडर के माध्यम से लोड किया जाता है। कक्षा को बाइट ऐरे से व्याख्यायित करने के बाद, VM स्थिर {} - ब्लॉक को निष्पादित करता है । यह पूरा रहस्य है: स्थैतिक-ब्लॉक को केवल एक बार कहा जाता है, दिए गए पैकेज के दिए गए वर्ग (नाम) को इस एक वर्ग लोडर द्वारा लोड किया जाता है।


3
सच नहीं। जब क्लास लोड होती है तो स्टेटिक वैरिएबल को स्टैटिक ब्लॉक के साथ इनिशियलाइज़ किया जाता है। घोषणा को विभाजित करने की आवश्यकता नहीं है।
क्रेग पी। मोटलिन

-5
public class Singleton {

    private static final Singleton INSTANCE = new Singleton();

    private Singleton(){
    if (INSTANCE != null)
        throw new IllegalStateException (“Already instantiated...”);
}

    public synchronized static Singleton getInstance() { 
    return INSTANCE;

    }

}

जैसा कि हमने getInstance से पहले सिंक्रोनाइज़्ड कीवर्ड को जोड़ दिया है, हमने उस स्थिति में दौड़ की स्थिति से बचा लिया है जब दो धागे एक ही समय में getInstance को कॉल करते हैं।

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