هل تزامن Java الكلمة الرئيسية تدفق ذاكرة التخزين المؤقت؟

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

سؤال

جافا 5 وما فوق فقط. افترض جهاز كمبيوتر متعدد الذاكرة مشترك (ربما تستخدم واحدة الآن).

فيما يلي رمز لتهيئة كسول للفرد:

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

يفعل instance يجب إعلانها volatile من أجل منع المحسن من إعادة كتابة GetInstance () على النحو التالي (والذي سيكون صحيحًا في برنامج متسلسل):

public static MySingleton getInstance() {
  if (instance == null) {
    synchronized (MySingleton.class) {
      // instance must be null or we wouldn't be here  (WRONG!)
      instance = new MySingleton();
    }
  }
}

على افتراض أن المحسن لا يعيد كتابة الرمز ، إذا instance لم يعلن volatile هل ما زال مضمون أن يتم مسح الذاكرة عند synchronized يتم الخروج من الكتلة ، وقراءة من الذاكرة عندما تكون synchronized تم إدخال كتلة؟

تحرير: لقد نسيت جعل getInstance () ثابت. لا أعتقد أن هذا يغير صحة الإجابات ؛ أنتم جميعا عرفوا ما قصدته.

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

المحلول

نعم، instance يجب إعلانه volatile. حتى ذلك الحين ، يُنصح بعدم استخدام القفل المزدوج. إنه (أو أن يكون دقيقًا ، نموذج ذاكرة Java) يستخدم في عيب خطير سمح بنشر الكائنات المنفذة جزئيًا. تم إصلاح هذا في Java5 ، لا يزال DCL هو تعبير قديم ولا توجد حاجة لاستخدامه بعد الآن - استخدم المصطلح حامل تهيئة كسول في حين أن.

من تزامن جافا في الممارسة, ، القسم 16.2:

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

تمكنت التغييرات اللاحقة في JMM (Java 5.0 وما بعدها) من DCL من العمل إذا resource مصنوع volatile, ، وتأثير الأداء في هذا الصغر منذ ذلك الحين volatile عادة ما تكون القراءات أكثر تكلفة قليلاً من القراءات غير المتطايرة. ومع ذلك ، هذا هو المصطلح الذي مرت فائدته إلى حد كبير - القوى التي حفزته (التزامن البطيء غير المتواصل ، بدء تشغيل JVM البطيء) لم تعد في اللعب ، مما يجعلها أقل فعالية كتحسين. يوفر المصطلح لحامل التهيئة الكسول نفس الفوائد وأسهل فهمه.

نصائح أخرى

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

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

هناك تعبير أكثر أمانًا وأكثر قابلية للقراءة للتشغيل البطيء لـ Java Singletons:

class Singleton {

  private static class Holder {
    static final Singleton instance = create();
  }

  static Singleton getInstance() { 
    return Holder.instance; 
  }

  private Singleton create() {
    ⋮
  }

  private Singleton() { }

}

إذا كنت تستخدم نمط القفل المزدوج المطوّل ، فيجب عليك إعلان الحقل volatile, ، كما لاحظ الآخرون بالفعل.

لا ، لا يجب أن تكون متقلبة. نرى كيف تحل إعلان "القفل المزدوج المكسور" في جافا؟

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

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