java طرق - ما هي الطريقة الفعالة لتنفيذ نمط مفرد في جافا؟





ادخال محلولة (25)


حقا النظر في السبب في أنك بحاجة إلى المفرد قبل كتابته. هناك جدل شبه ديني حول استخدامها ، والتي يمكنك أن تتعثر بسهولة أكثر إذا كنت google المفردات في جاوة.

أنا شخصياً أحاول أن أتجنب المفردات قدر المستطاع لأسباب كثيرة ، ومرة ​​أخرى يمكن العثور على معظمها من خلال مفردات googling. أشعر أنه في كثير من الأحيان يتم إساءة استخدام المفردات لأنها سهلة الفهم من قبل الجميع ، يتم استخدامها كآلية للحصول على البيانات "العالمية" في تصميم OO ويتم استخدامها لأنه من السهل التحايل على إدارة دورة حياة الكائن (أو حقا التفكير في كيف يمكنك القيام A من داخل B). انظر إلى أشياء مثل انقلاب التحكم (IoC) أو Dependency Injection (DI) لساحة وسطى لطيفة.

إذا كنت في حاجة حقاً إلى ذلك ، فستكون لدى wikipedia مثالاً جيدًا للتطبيق السليم لفرد واحد.

ما هي الطريقة الفعالة لتنفيذ نمط مفرد في جافا؟




I still think after java 1.5, enum is the best available singleton implementation available as it also ensures that even in the multi threaded environments - only one instance is created.

public enum Singleton{ INSTANCE; }

and you are done !!!




لا تنس أن Singleton هو فقط Singleton لـ Classloader الذي قام بتحميله. إذا كنت تستخدم لوادر متعددة (حاويات) كل COULD لها نسختها الخاصة من Singleton.




إذا كنت لا تحتاج إلى تحميل كسول ثم حاول ببساطة

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();
    }
}

إذا كنت تريد التحميل البطيء وكنت تريد أن يكون Singleton آمنًا ، فحاول استخدام نمط التدقيق المزدوج

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 بالكامل أو إنشاء سجل لكل Singletons الخاص بك.




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;

    }

}

كما قمنا بإضافة الكلمة الأساسية Synchronized قبل getInstance ، قمنا بتجنب حالة السباق في الحالة عندما استدعاء اثنين مؤشرات ترابط getInstance في نفس الوقت.




ننسى التهيئة البطيئة ، إنها إشكالية للغاية. هذا هو أبسط حل:

public class A {    

    private static final A INSTANCE = new A();

    private A() {}

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



أنا محير من بعض الإجابات التي تقترح DI كبديل لاستخدام مفردات؛ هذه هي مفاهيم غير ذات صلة. يمكنك استخدام DI لحقن حالات فردية أو فردية (مثال لكل مؤشر ترابط). على الأقل هذا صحيح إذا كنت تستخدم Spring 2.x ، لا أستطيع التحدث عن أطر عمل DI أخرى.

لذا فإن إجابتي على OP ستكون (في كل ما عدا رمز العينة الأكثر تافها):

  1. استخدم إطار عمل DI مثل الربيع ، ثم
  2. اجعلها جزءًا من تهيئة DI لديك ، سواء كانت تبعياتك مفردة أو طلب نطاق أو جلسة عمل أو أي شيء.

يمنحك هذا الأسلوب بنية منفصلة مفصولة (وبالتالي مرنة وقابلة للاختبار) حيث إذا كنت تريد استخدام singleton هو تفصيل تطبيق يمكن عكسه بسهولة (بشرط أن تكون أي مفردات تستخدمها خياريًا ، بالطبع).




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");
    }
}



تحتاج double-checking المصطلح إذا كنت بحاجة إلى تحميل متغير المثيل لفصل دراسي. إذا كنت في حاجة إلى تحميل متغير ثابت أو مفردة المفرد ، فأنت تحتاج إلى التهيئة على لغة حامل الطلب .

بالإضافة إلى ذلك ، إذا احتاج المفرد إلى أن يكون seriliazble ، يجب أن تكون جميع الحقول الأخرى عابرة ويجب أن يتم تنفيذ methodResolve () من أجل الحفاظ على كائن مفرد unariant. خلاف ذلك ، في كل مرة يتم إلغاء تسلسل الكائن ، سيتم إنشاء نسخة جديدة من الكائن. ما يقوم readResolve () باستبدال الكائن الجديد الذي يقرأ بواسطة readObject () ، والذي أجبر ذلك الكائن الجديد على أن يتم جمع البيانات المهملة حيث لا يوجد متغير يشير إليها.

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



For JSE 5.0 and above take the Enum approach, otherwise use static singleton holder approach ( (a lazy loading approach described by Bill Pugh). Latter solution is also thread-safe without requiring special language constructs (ie volatile or synchronized).




To achieve this ( TRUE Singleton) ,

  1. Make sure that your class is final . Others can't sub-class it and create one more instance
  2. Make your singleton object as private static final
  3. Provide private constructor and public getInstance() method.
  4. Make sure that this Singleton class is loaded by one ClassLoader only
  5. override readResolve( ) method and return same instance.

Useful links: All answers in this post +

Singleton_pattern : Initialization-on-demand holder idiom from wikipedia

journaldev article

Demonstration of various methods of achieving Singleton

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

    public static Singleton getInstance(){
        return instance; 
    }
    public enum EnumSingleton {
        INSTANCE;   
    }
    private Object readResolve()  {
        return instance;
    }
    public static void main(String args[]){
        System.out.println("Singleton:"+Singleton.getInstance());
        System.out.println("Enum.."+EnumSingleton.INSTANCE);
        System.out.println("Lazy.."+LazySingleton.getInstance());
    }
}
final class  LazySingleton {
    private LazySingleton() {}
    public static LazySingleton getInstance() {
        return LazyHolder.INSTANCE;
    }
    private static class LazyHolder {
        private static final LazySingleton INSTANCE = new LazySingleton();
    }
    private Object readResolve()  {
        return LazyHolder.INSTANCE;
    }
}

انتاج:

Singleton:[email protected]
Enum..INSTANCE
[email protected]



التعداد المفرد

تستخدم أبسط طريقة لتطبيق Singleton الآمن مؤشر ترابط التعداد

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

يعمل هذا الرمز منذ إدخال Enum في Java 1.5

فحص مزدوج قفل

If you want to code a “classic” singleton that works in a multithreaded environment (starting from Java 1.5) you should use this one.

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 ;
  }
}

This is not thread-safe before 1.5 because the implementation of the volatile keyword was different.

Early loading Singleton (works even before Java 1.5)

This implementation instantiates the singleton when the class is loaded and provides thread safety.

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");
  }

}



إخلاء المسؤولية: لقد لخصت كل الإجابات الرائعة وكتبتها في كلماتي.

أثناء تنفيذ Singleton لدينا 2 خيارات
1. تحميل كسول
2. التحميل المبكر

التحميل البطيء يضيف عبئا قليلا (الكثير من الصدق) بحيث لا تستخدمه إلا عندما يكون لديك جسم كبير جدا أو كود بناء ثقيل ولديك أيضا طرق أو حقول ثابتة أخرى يمكن الوصول إليها قبل الحاجة إلى مثيل ، ثم عندئذ فقط تحتاج إلى استخدام التهيئة البطيئة.خيار الاختيار المبكر هو خيار جيد.

معظم طريقة بسيطة لتنفيذ Singleton هو

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

الآن نحن على يقين حول موضوع الشر ولكن ماذا عن التسلسل القاسي؟ علينا أن نتأكد من أنه في حين أن de-serialiaztion لا يتم إنشاء أي كائن جديد

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() الطريقة 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 بالفعل ، وهذا يعتني FooLoader البطيئة ،

وهل يضمن الخيط الآمن.

لقد جئنا حتى الآن ، هنا أفضل طريقة لتحقيق كل ما قمنا به هو أفضل طريقة ممكنة

 public enum Foo {
       INSTANCE;
   }

التي سوف تعامل داخليا مثل

public class Foo {

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

هذا هو أنه لا مزيد من الخوف من التسلسل والخيوط والرموز القبيحة. أيضا يتم تهيئة ENUMS المفرد بتكاسل .

هذا النهج مكافئ وظيفيا لنهج المجال العام ، إلا أنه أكثر إيجازا ، ويوفر آلية التسلسل مجانا ، ويوفر ضمان ironclad ضد تكرار متعددة ، حتى في مواجهة تسلسل متطورة أو هجمات التفكير. وبينما لم يتم بعد اعتماد هذا النهج على نطاق واسع ، فإن نمط التعداد ذي العنصر الواحد هو أفضل طريقة لتنفيذ المفرد.

- جوشوا بلوخ في "جاوة الفعالة"

الآن ربما تكون قد أدركت سبب اعتبار ENUMS كأفضل طريقة لتطبيق Singleton وشكرًا على صبرك :)
تحديثه على blog .




خيط آمن في Java 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 هنا. :) من المهم لأنه بدونها ، لا يتم ضمان خيوط أخرى بواسطة JMM (Java Memory Model) لرؤية التغييرات على قيمتها. التزامن لا يعتني بذلك - فقط تسلسل الوصول إلى تلك الكتلة من التعليمات البرمجية.

EDIT 2 : إجابةBno تفصّل المقاربة التي أوصى بها Bill Pugh (FindBugs) وهي قابلة للنقاش بشكل أفضل. اذهب اقرأ وتصوت إجابته أيضا.




النسخة 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;
    }
}

تحميل كسول ، موضوع آمنة مع عدم حجب ، والأداء العالي.




استخدم التعداد:

public enum Foo {
    INSTANCE;
}

أوضح جوشوا بلوخ هذا النهج في حديثه عن " إعادة تشغيل جافا" في Google I / O 2008: رابط إلى الفيديو . راجع أيضًا الشرائح 30-32 من عرضه التقديمي ( effective_java_reloaded.pdf ):

الطريقة الصحيحة لتنفيذ Serializable Singleton

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

تحرير: يقول جزء على الإنترنت من "Java الفعالة" :

"هذا النهج مكافئ وظيفيا لنهج المجال العام ، إلا أنه أكثر إيجازا ، ويوفر آلية التسلسل مجانا ، ويوفر ضمان ironclad ضد محاولات متعددة ، حتى في مواجهة تسلسل متطورة أو هجمات التفكير. في حين أن هذا النهج قد بعد أن يتم تبنيها على نطاق واسع ، فإن نوع التعداد المفرد هو أفضل طريقة لتطبيق المفرد . "




فيما يلي 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;
    }
}



simplest singleton class

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



This is how to implement a simple 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;
    }
}

This is how to properly lazy create your 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();
    }
}



طرق مختلفة لجعل كائن منفرد:

  1. حسب Joshua Bloch - Enum سيكون الأفضل.

  2. يمكنك استخدام قفل الاختيار المزدوج أيضا.

  3. حتى الطبقة الداخلية الثابتة يمكن استخدامها.




الحل الذي نشره Stu Thompson صالح في Java5.0 والإصدارات الأحدث. لكنني أفضل عدم استخدامها لأنني أعتقد أنها عرضة للخطأ.

من السهل أن تنسى البيان المتقلب ويصعب فهم سبب ضرورة ذلك. دون المتطايرة هذا الرمز لن يكون خيط آمنة بعد الآن بسبب antipattern قفل مزدوج التحقق. انظر المزيد عن هذا في الفقرة 16.2.4 من التزامن جافا في الممارسة . باختصار: هذا النمط (قبل Java5.0 أو بدون العبارة المتقلبة) قد يعيد مرجعًا إلى كائن Bar (لا يزال) في حالة غير صحيحة.

تم اختراع هذا النمط لتحسين الأداء. لكن هذا في الحقيقة لم يعد مصدر قلق حقيقي بعد الآن. رمز التهيئة البطيئة التالية سريعًا ، والأهم من ذلك أنه من الأسهل قراءة.

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

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



أستخدم إطار Spring لإدارة My singletons. إنها لا تفرض "الطبقة المفردة" للفئة (التي لا يمكنك فعلها على أي حال إذا كان هناك العديد من لوادر التحميل) ولكنها توفر طريقة سهلة لبناء وتكوين مصانع مختلفة لإنشاء أنواع مختلفة من الكائنات.




Sometimes a simple " static Foo foo = new Foo(); " is not enough. Just think of some basic data insertion you want to do.

On the other hand you would have to synchronize any method that instantiates the singleton variable as such. Synchronisation is not bad as such, but it can lead to performance issues or locking (in very very rare situations using this example. The solution is

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;
    }

}

الآن ماذا يحدث؟ The class is loaded via the class loader. Directly after the class was interpreted from a byte Array, the VM executes the static { } - block. that's the whole secret: The static-block is only called once, the time the given class (name) of the given package is loaded by this one class loader.




تأكد من أنك حقا بحاجة إليها. هل جوجل للبحث عن "نمط مكافحة المفرد" لرؤية بعض الحجج ضدها. لا يوجد شيء خطأ في ذلك ، لكنني أعتقد أنه مجرد آلية لفضح بعض الموارد / البيانات العالمية لذا تأكد من أن هذه هي أفضل طريقة. على وجه الخصوص ، وجدت حقنة التبعية أكثر فائدة خاصة إذا كنت تستخدم أيضًا اختبارات الوحدة لأن شركة DI تسمح لك باستخدام الموارد الساخرة لأغراض الاختبار.




في ARM ، لا يوجد فرع مطلوب ، لأن كل تعليمة لديها حقل شرط 4 بت ، والذي يتم اختباره بتكلفة صفر. هذا يلغي الحاجة إلى الفروع القصيرة ، ولن يكون هناك أي توقع للفرع. لذلك ، سيتم تشغيل الإصدار الذي تم فرزه بشكل أبطأ من الإصدار غير المُفرط على ARM ، بسبب مقدار الحمل الزائد للفرز. ستبدو الحلقة الداخلية كالتالي:

MOV R0, #0     // R0 = sum = 0
MOV R1, #0     // R1 = c = 0
ADR R2, data   // R2 = addr of data array (put this instruction outside outer loop)
.inner_loop    // Inner loop branch label
    LDRB R3, [R2, R1]     // R3 = data[c]
    CMP R3, #128          // compare R3 to 128
    ADDGE R0, R0, R3      // if R3 >= 128, then sum += data[c] -- no branch needed!
    ADD R1, R1, #1        // c++
    CMP R1, #arraySize    // compare c to arraySize
    BLT inner_loop        // Branch to inner_loop if c < arraySize




java singleton design-patterns