سؤال

مذكور في نموذج ذاكرة Java ما يلي:عندما يخرج مؤشر ترابط من كتلة متزامنة كجزء من تحرير الشاشة المرتبطة، يتطلب JMM مسح ذاكرة التخزين المؤقت للمعالج المحلي إلى الذاكرة الرئيسية.بصورة مماثلة، كجزء من الحصول على الشاشة عند الدخول إلى كتلة متزامنة، يتم إبطال ذاكرة التخزين المؤقت المحلية بحيث تذهب عمليات القراءة اللاحقة مباشرة إلى الذاكرة الرئيسية وليس ذاكرة التخزين المؤقت المحلية.

فلماذا في هذا الكود يجب أن أعلن أن المثيل متقلب لأنه عندما يدخل الخيط الثاني فإن كتلة المزامنة ستنتقل مباشرة إلى الذاكرة الرئيسية ؟؟

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

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

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

المحلول

حالة السباق هي كالتالي

  1. يرى الخيط أ instance == NULL ويقوم بتشغيل هذا الرمز instance = new MySingleton();.الكتابة إلى instance مرئي ولكن يكتب إلى MySingleton ليس بعد.

  2. يرى الموضوع B instance != NULL ويبدأ العمل على سبيل المثال.

  3. يعمل مؤشر الترابط B الآن على كائن لا يمكن رؤية بنائه.

تحضير instance volatile يحل المشكلة حيث أن مواصفات ذاكرة JDK، اعتبارًا من JDK5، تضمن أن الكتابة إلى الكائنات غير المتطايرة لن يتم رؤيتها خارج الترتيب فيما يتعلق بالكتابة إلى كائن متطاير.لذلك أي موضوع الذي يرى instance != NULL يجب أن نرى المثيل نفسه.

نصائح أخرى

يجب عليك أن تعلن أنه متقلب وإلا فليس هناك ما يضمن أن مكالمتين لـ getInstance() ستعيد نفس المثيل.

ليس هناك ما يضمن الوصول إلى الذاكرة الرئيسية، فقط قيمة متسقة مع ذاكرة التخزين المؤقت.أي.جميع المواضيع سوف ترى نفس القيمة.

بالمناسبة:أنت تعلم بالطبع أن الأمر أكثر تعقيدًا بكثير مما هو مطلوب.كل ما تحتاجه هو

public enum MySingleton {
     INSTANCE;
}

يفعل نفس الشيء كثيرًا.

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