English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

نموذج التصميم Singleton في Java

نمط Singleton في تصميم Java

مقدمة:

في عملية تطوير البرمجيات، غالبًا ما نحتاج إلى بعض الكائنات فقط مرة واحدة، مثل: حاوية الأنماط (threadpool)، المخزن المؤقت (cache)، نافذة الحوار، إعدادات التفضيلات، وما إلى ذلك. قد يؤدي إنشاء عدة نماذج من هذه الكائنات إلى بعض المشاكل غير الضرورية، مثل: سلوك البرنامج غير الطبيعي، استهلاك الموارد الزائد، وما إلى ذلك. في هذه الحالة، يمكن للنمط Singleton ضمان وجود نموذج واحد فقط من الفئة، وتوفير نقطة الوصول العالمية. فيما يلي مناقشة كيفية تحقيق نمط Singleton باستخدام نموذج Singleton البسيط.

/* 那么是否可以在使用延迟实例化的同时又不会造成线程不安全且增加访问效率呢。接下来就用双重检查加锁来改进一下。 */
 * الفئة الأكثر كلاسيكية لنموذج Singleton
 */
public class Singleton {
  // يتم تعيينها كمتغيرات ثابتة لالتقاط نموذج Singleton الفريد
  private static Singleton singleInstance;
  private Singleton(){
    // يتم تعريف طريقة التركيب كخاصة، لذا يمكن الوصول إليها فقط داخل فئة Singleton
  }
  /*
   * الحصول على نموذج Singleton، إذا لم يتم إنشاء نموذج بعد، فإنه يقوم بإنشاء نموذج جديد وإرجاع هذا المثيل
   */
  public static Singleton getInstance(){
    إذا (singleInstance == null) {
      singleInstance = new Singleton();
    }
    return singleInstance;
  }
  return singletongA;
}

من المثال السابق يمكن ملاحظة أن Singleton تدير عملية إنشاء مثيلاتها الخاصة، وتقدم نقطة الوصول العالمية، وهي طريقة getInstance() المعدة كحالة ثابتة، عند استخدام Singleton في فئة أخرى، فإنها تعود نموذج Singleton. لهذه نموذج Singleton ميزة هي تأخير الإنشاء، بمعنى آخر تأخير التوليد، هو إنشاء المثيل عند الحاجة، وليس عند بدء تحميل هذا الفئة، مما يمكن من تجنب إهدار الأداء. على سبيل المثال، بعض الأجسام لا تحتاج إلى استخدامها عند بدء البرنامج، أو لم يتم استخدامها أثناء تنفيذ البرنامج. ولكن لهذا المثال عيب آخر، وهو عدم الأمان في السلسلة من التشغيل. لأنه إذا كان هناك عدة سلاسل تشغيل تنفذ طريقة getInstance() في نفس الوقت، ولم يتم إنشاء نموذج Singleton بعد، فإن جميع السلاسل تشعر بأن singleInstance هو null، وستقوم بإنشاء Singleton، مما يؤدي إلى ظهور عدة نماذج Singleton، مما لا يتوافق مع الفكرة الأساسية لنموذج Singleton. لذا، قد تحتاج إلى تحسينه في المستقبل

public class SingletonA {
  private static SingletonA singletongA;
  private SingletonA(){
  }
  /*
   * يضيف الكلمة المفتاحية synchronized لتحويل طريقة getSingletonA إلى طريقة سينكرونizada
   */
  public static synchronized SingletonA getInstanceA(){
    إذا (singletongA == null) {
      singletongA = new SingletonA();
    }
    singletongA = new SingletonA();
  }
  return singletongA;
}

// 其他方法

从这个例子上看增加了synchronized可以使getInstanceA()变成一个同步的方法,这时线程在进入这个方法之前就需要等待其他线程离开这个方法才能进入,也就使得该方法只能同时存在一个线程在执行它。

可能差不多问题解决了,但是要知道同步方法是会影响程序执行效率的,在此例子中我们只是为了解决第一个例子中第一次执行getInstance()方法不会产生多个实例,而这个例子中却会导致每次需要实例时都会调用getInstanceA()同步方法,而在已经有实例之后的调用synchronized就会是累赘,因为我们已经无需担心这个单例类会再次被创建出新的实例。因此我们还需要做一下改进。

既然上面说到延迟实例化,那么如果是不用的话那就简单多了。
  public class SingletonB {
  // 在静态初始化器(static initializen)中创建单例,保证线程安全
  private static SingletonB singletonB = new SingletonB();
    private SingletonB(){
  }
  // 构造函数
    public static SingletonB getInstaceB(){
    // 已经实例化了,直接使用它
  }
}

return singletonB;

上面的这种做法是在JVM加载这个类时马上创建一个实例,因为JVM会在线程访问这个实例之前就创建出该实例,因此线程是安全的。但这相较于延迟实例化而言可能会出现资源的浪费。而且如果此类较大的情况下会时程序初始化时间加长。

/* 那么是否可以在使用延迟实例化的同时又不会造成线程不安全且增加访问效率呢。接下来就用双重检查加锁来改进一下。 */
 /* 双重锁单例模式 */
 */
public class SingletonC {
  private volatile static SingletonC singletonC;
  private SingletonC(){
  }
  public static SingletonC getInstanceC(){
    إذا (singletonC == null) {
      ynchronized (SingletonC.class) {
        إذا (singletonC == null) {
          singletonC = new SingletonC();
        }
      }
    }
    تعود singletonC;
  }
}

النموذج المذكور أعلاه هو التحقق من المثال أولاً، إذا لم يكن موجودًا، يدخل في منطقة التنفيذ المتزامن، بعد الدخول إلى منطقة التنفيذ المتزامن، يتم التحقق مرة أخرى، إذا كان لا يزال null، يتم إنشاء المثال، لذا فإن سطر singletonC = new SingletonC() سيتم تنفيذه مرة واحدة فقط، وسيتم العودة إلى getInstanceC() بعد ذلك مباشرة، لأن هناك مثالاً مباشرة، لذا فإنه، باستثناء الاستدعاء الأول، لن يتم السفر إلى منطقة التنفيذ المتزامن مثل المثال الثاني، مما يمكن من تقليل وقت تنفيذ getInstanceC(). يبدو أن هناك كلمة مفتاحية volatile، وظيفتها هي جعل singletonC مرئيًا لجميع الأطراف بعد التشغيل، حيث يمكن للعديد من الأطراف التعامل مع متغير SingletonC بشكل صحيح. ولكن يجب الانتباه إلى أن كلمة المفتاحية volatile يمكن استخدامها فقط في Java 5 وما بعدها، إذا تم استخدامها في إصدارات سابقة قد يؤدي إلى فشل التحقق المزدوج.

عند استخدام نموذج Singleton، إذا كان هناك عدة محمولات كلاس (classloader) يجب تحديد المحمول بشكل مستقل، و تحديد استخدامه محمولاً واحداً. لأن كل محمول كلاس يحدد مساحة اسمية، قد يقوم محمولان مختلفان بتحميل نفس الكلاس، مما يؤدي إلى إنشاء عدة نسخ من كلاس Singleton.

شكراً على القراءة، آمل أن يساعدكم هذا، شكراً لدعمكم لهذا الموقع!

بيان: محتوى هذا المقال تم جمعه من الإنترنت، ويتمتع المالك الأصلي بحقوق النشر، ويتم جمع المحتوى من قبل المستخدمين عبر الإنترنت الذين يقدمون مساهماتهم الخاصة ويتم تحميلها بشكل مستقل، ويتمتع هذا الموقع بلا حقوق ملكية، ولا يتم تعديل المحتوى بشكل إنساني، ولا يتحمل هذا الموقع أي مسؤولية قانونية. إذا كنت قد وجدت محتوى يشتبه في حقوق النسخ، فلا تتردد في إرسال بريد إلكتروني إلى: notice#oldtoolbag.com (عند إرسال البريد الإلكتروني، يرجى استبدال '#' بـ '@') للإبلاغ، وتقديم الدليل على ذلك، وإذا تم التحقق من ذلك، فإن هذا الموقع سيقوم بإزالة المحتوى المشبوه فوراً.

سيحبك