هل تزامن Java الكلمة الرئيسية تدفق ذاكرة التخزين المؤقت؟
-
25-09-2019 - |
سؤال
جافا 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
مجالات.