هل يمكن إعادة بناء Java Singleton بشكل متكرر في WebSphere 6؟

StackOverflow https://stackoverflow.com/questions/225895

  •  03-07-2019
  •  | 
  •  

سؤال

أحاول تعقب مشكلة في نظامنا والكود التالي يقلقني.يحدث ما يلي في طريقة doPost() الخاصة بنا في servlet الأساسي (تم تغيير الأسماء لحماية المذنب):

...
if(Single.getInstance().firstTime()){
   doPreperations();
}
normalResponse();
...

يبدو المفرد "Single" كما يلي:

private static Single theInstance = new Single();

private Single() {
...load properties...
}

public static Single getInstance() {
    return theInstance;
}

من خلال الطريقة التي تم بها تعيين ذلك لاستخدام مُهيئ ثابت بدلاً من التحقق من وجود theInstance فارغًا في طريقة getInstance()، هل يمكن إعادة بناء هذا مرارًا وتكرارًا؟

ملاحظة: نحن نقوم بتشغيل WebSphere 6 مع التطبيق على Java 1.4

هل كانت مفيدة؟

المحلول

ولقد وجدت هذا في الموقع الشمس:

<اقتباس فقرة>   

متعددة في وقت واحد سنغلتونس تحميلها من قبل مختلف الدرجة تحميل

     

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

     

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

     

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

استشهاد .

وبالإضافة إلى هذه القضية المذكورة أعلاه، إذا لم يتم مزامنة firstTime()، هل يمكن أن يكون القضايا خيوط هناك أيضا.

نصائح أخرى

وأي أنها لن تحصل على بني مرارا وتكرارا. انها ثابتة، لذلك سوف يتم بناؤها مرة واحدة فقط، على حق عندما لمست الطبقة لأول مرة من قبل Classloader.

واستثناء فقط - إذا كنت يحدث لديها Classloaders متعددة

و(من GeekAndPoke ):

وكما ذكر آخرون، سيتم تشغيل مهيئ ثابت فقط مرة واحدة في classloader.

وشيء واحد أود أن نلقي نظرة على هو الأسلوب firstTime() - لماذا لا يمكن التعامل معها العمل في doPreparations() داخل المفردة نفسها

ويبدو وكأنه مجموعة سيئة من التبعيات.

وعلى الاطلاق لا يوجد فرق بين استخدام مهيئ ثابت والتهيئة كسول. في الواقع انه من الاسهل بكثير لخبط التهيئة كسول، التي تفرض أيضا التزامن. وJVM يضمن أن مهيئ ثابت يتم تشغيل دائما قبل الوصول إلى الصف، وأنه لن يحدث مرة واحدة فقط.

وقال JVM لا يضمن أن صفك سيتم تحميلها مرة واحدة فقط. ولكن حتى إذا تم تحميل أكثر من مرة، وتطبيق الويب الخاص بك لا تزال ترى سوى المفرد ذات الصلة، كما سيتم تحميلها إما في classloader تطبيق ويب أو الأم. إذا كان لديك العديد من تطبيقات الويب نشر، ثم سوف يطلق firstTime () مرة واحدة لكل تطبيق.

والأشياء الأكثر وضوحا للتحقق هي أن firstTime () يحتاج إلى أن تكون متزامنة وأن يتم تعيين العلامة firstTime قبل أن تغادر هذه الطريقة.

لا، لن يتم إنشاء نسخ متعددة من "مفردة".(ستتم زيارة مشكلة Classloader لاحقًا)

تم وصف التنفيذ الذي حددته بأنه "التهيئة المتلهفة" في كتاب براينت جويتز - "جافا التزامن في الممارسة العملية'.

public class Single
{
    private static Single theInstance = new Single();

    private Single() 
    { 
        // load properties
    }

    public static Single getInstance() 
    {
        return theInstance;
    }
}

ومع ذلك، فإن الرمز ليس الذي تريده.يحاول الكود الخاص بك تنفيذ التهيئة البطيئة بعد إنشاء المثيل.يتطلب هذا من كافة مكتبات العميل إجراء 'firstTime()/doPreparation()' قبل استخدامه.ستعتمد على العميل للقيام بالشيء الصحيح الذي يجعل الكود هشًا للغاية.

يمكنك تعديل الكود كما يلي حتى لا يكون هناك أي كود مكرر.

public class Single
{
    private static Single theInstance = new Single();

    private Single() 
    { 
        // load properties
    }

    public static Single getInstance() 
    {   
        // check for initialization of theInstance
        if ( theInstance.firstTime() )
           theInstance.doPreparation();

        return theInstance;
    }
}

لسوء الحظ، يعد هذا تنفيذًا سيئًا للتهيئة البطيئة ولن يعمل هذا في بيئة متزامنة (مثل حاوية J2EE).

هناك العديد من المقالات المكتوبة حول تهيئة Singleton، وتحديدًا حول نموذج الذاكرة. جي إس آر 133 معالجة العديد من نقاط الضعف في نموذج ذاكرة Java في Java 1.5 و1.6.

مع Java 1.5 و1.6، لديك عدة خيارات وهي مذكورة في الكتاب.جافا فعالة" بقلم جوشوا بلوخ .

  1. التهيئة السريعة، مثل ما ورد أعلاه [EJ Item 3]
  2. مصطلح فئة حامل التهيئة الكسول [EJ Item 71]
  3. نوع التعداد [EJ Item 3]
  4. قفل مزدوج مع حقل ثابت "متقلب" [EJ Item 71]

سيعمل الحل 3 و4 فقط في Java 1.5 والإصدارات الأحدث.لذا فإن الحل الأفضل هو رقم 2.

هنا هو التنفيذ الزائف.

public class Single
{
    private static class SingleHolder
    {
        public static Single theInstance = new Single();
    }

    private Single() 
    { 
        // load properties
        doPreparation();
    }

    public static Single getInstance() 
    {
        return SingleHolder.theInstance;
    }
}

لاحظ أن 'doPreparation()' موجود داخل المنشئ لذا نضمن لك الحصول على المثيل الذي تمت تهيئته بشكل صحيح.أيضًا، أنت تستعيد تحميل فئة JVM البطيئة ولا تحتاج إلى أي مزامنة "getInstance ()".

الشيء الوحيد الذي لاحظته هو أن الحقل الثابت هو المثيل ليس النهائي'. لا يحتوي المثال الموجود في Java Concurrency على "نهائي" ولكن EJ يحتوي عليه.ربما يستطيع جيمس إضافة المزيد من الألوان إلى إجابته على "محمل الفصل" ومتطلبات "النهائي" لضمان الصحة،

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

اليك مثال بسيط.

ملف:أ.جافا

public class A
{
    final static String word = "Hello World";
}

ملف:ب.جافا

public class B
{
    public static void main(String[] args) {
        System.out.println(A.word);
    }
}

بعد تجميع كل من A.java وB.java، يمكنك تغيير A.java إلى التالي.

ملف:أ.جافا

public class A
{
    final static String word = "Goodbye World";
}

يمكنك إعادة ترجمة 'A.java' وإعادة تشغيل B.class.الناتج الذي ستحصل عليه هو

Hello World

أما بالنسبة لمشكلة أداة تحميل الفصل، فالإجابة هي نعم، حيث يمكن أن يكون لديك أكثر من مثيل Singleton في أدوات تحميل فئة متعددة.يمكنك العثور على مزيد من المعلومات حول ويكيبيديا.هناك أيضًا مقالة محددة حول مجال الويب.

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

وبطبيعة الحال، إذا لم تحميلها من تحميل الطبقة منفصلة تحصل متعددة "سنغلتونس"، ولكن هذا وجود قيود على كل تعبير المفرد في جاوة.

تعديل : في وfirstTime () وdoPreparations () بت لا تبدو المشتبه به على الرغم من. لا يمكن لهم أن انتقل إلى منشئ مثيل المفرد؟

لا - التهيئة الثابتة لـ instance لن يتم ذلك إلا مرة واحدة.شيئان يجب مراعاتهما:

  • هذا ليس آمنًا لمؤشر الترابط (لم يتم "نشر" المثيل على الذاكرة الرئيسية)
  • لك firstTime ربما يتم استدعاء الطريقة عدة مرات، ما لم تتم مزامنتها بشكل صحيح

في نظرية سيتم بناؤها مرة واحدة فقط. ومع ذلك، يكسر هذا النمط في مختلف خوادم التطبيقات، حيث يمكنك الحصول على مثيلات متعددة الطبقات "المفرد" (لأنها ليست موضوع آمن).

وكما تم critized نمط المفرد الكثير. انظر على سبيل المثال سينغلتون أنا أحبك، ولكن كنت تجلب لي أسفل

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


public class Single {

private static class SingleHolder {
   private static final Single INSTANCE = new Single();
}

private Single() {
...load properties...
}

public static Single getInstance() {
    return SingleHolder.INSTANCE;
}

}

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

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

تحليل في رمز أدناه يمكنك أن ترى كيف يحصل مثيل لمرة واحدة فقط (المرة الأولى التي يطلق منشئ)

وتاريخ حزمة؛

وjava.util.Date الاستيراد؛

والطبقة العامة USDateFactory تنفذ DateFactory {     ساكنة الخاص USDateFactory usdatefactory = فارغة؛

private USDateFactory () { }

public static USDateFactory getUsdatefactory() {
    if(usdatefactory==null) {
        usdatefactory = new USDateFactory();
    }
    return usdatefactory;
}

public String getTextDate (Date date) {
    return null;
}

public NumericalDate getNumericalDate (Date date) {
    return null;
}

و}

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top