سؤال

القراءة "جافا التزامن في الممارسة" يوجد هذا الجزء في القسم 3.5:

public Holder holder;
public void initialize() {
     holder = new Holder(42);
}

إلى جانب واضحة الخيط خطرا على السلامة إنشاء مثيلين من Holder, الكتاب المطالبات المحتملة النشر يمكن أن تحدث المشكلة.

وعلاوة على ذلك ، Holder فئة مثل

public Holder {
    int n;
    public Holder(int n) { this.n = n };
    public void assertSanity() {
        if(n != n)
             throw new AssertionError("This statement is false.");
    }
}

وهو AssertionError يمكن طرح!

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

هل هذا ممكن ؟

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

المحلول

السبب في هذا هو الممكن هو أن جافا لديه ضعف الذاكرة نموذج.فإنه لا يضمن يأمر من يقرأ ويكتب.

هذه المشكلة بالذات يمكن أن تتكرر مع اثنين التالية التعليمات البرمجية المتكررة تمثل اثنين من المواضيع.

الموضوع: 1:

someStaticVariable = new Holder(42);

الموضوع 2:

someStaticVariable.assertSanity(); // can throw

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

  1. الوك الذاكرة إلى pointer1
  2. كتابة 42 إلى pointer1 عند الإزاحة 0
  3. كتابة pointer1 إلى someStaticVariable

لأن جافا لديه ضعف الذاكرة النموذج ، فمن الممكن تماما للحصول على التعليمات البرمجية لتنفيذ فعلا في الترتيب التالي من منظور الخيط 2:

  1. الوك الذاكرة إلى pointer1
  2. كتابة pointer1 إلى someStaticVariable
  3. كتابة 42 إلى pointer1 عند الإزاحة 0

مخيف ؟ نعم ولكن يمكن أن يحدث.

ما يعنيه هذا هو أنه على الرغم من الخيط 2 يمكن أن الكلمة الآن إلى assertSanity قبل n وقد حصلت على قيمة 42.فمن الممكن القيمة n أن تقرأ مرتين خلال assertSanity, مرة قبل عملية #3 يكمل ومرة بعد وبالتالي نرى اثنين من قيم مختلفة و رمي استثناء.

تحرير

وفقا جون السكيت, ، AssertionError migh لا تزال تحدث مع جافا 8 إلا إذا كان الحقل هو النهائي.

نصائح أخرى

جافا نموذج الذاكرة تستخدم أن يكون هذا التعيين Holder المرجعية قد تصبح مرئية قبل الإحالة إلى متغير داخل الكائن.

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

كائن يعتبر تماما تهيئة عندما منشئ التشطيبات.موضوع يمكن أن نرى فقط إشارة إلى كائن بعد أن الكائن قد تم تماما تهيئة مضمونة لمعرفة صحيح تهيئة القيم التي الكائن النهائي المجالات

ذلك المثال الخاص بك يمكن أن لا تزال تفشل كما n هو غير النهائية ، ولكن ينبغي أن يكون حسنا إذا جعل n النهائي.

بالطبع:

if (n != n)

يمكن أن تفشل بالتأكيد غير النهائية المتغيرات ، على افتراض JIT compiler لا تحسين بعيدا - إن العمليات هي:

  • جلب LHS:n
  • جلب RHS:n
  • قارن LHS و RHS

ثم قيمة يمكن أن تتغير بين اثنين جلب.

حسنا، في الكتاب فإنه ينص على كتلة التعليمات البرمجية الأول ما يلي:

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

والمشكلة هنا ليست حامل   الفئة نفسها، إلا أن حامل هو   لم تنشر بشكل صحيح. ومع ذلك،   حامل يمكن إجراء المناعي لائق   نشر بإعلان مجال ن   أن تكون المباراة النهائية، التي من شأنها أن تجعل حامل   غير قابل للتغيير. انظر القسم 3.5.2

ولكتلة التعليمات البرمجية الثاني:

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

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

وأعتقد JaredPar حققت الى حد كبير ذلك صراحة في تعليقه.

و(ملاحظة: لا تبحث عن الأصوات هنا - إجابات تسمح لمزيد من المعلومات أكثر تفصيلا من تعليقات)

والقضية الأساسية هي أنه بدون تزامن المناسبة، كيف يكتب إلى الذاكرة قد تظهر في المواضيع المختلفة. والمثال الكلاسيكي:

a = 1;
b = 2;

إذا كنت تفعل ذلك على موضوع واحد، وهو موضوع الثاني قد ترى ب مجموعة إلى 2 قبل أن يتم تحديد إلى 1. وعلاوة على ذلك، فمن الممكن أن يكون هناك مبلغ غير محدود من الوقت بين موضوع الثاني رؤية واحدة من تلك المتغيرات على المحدثة والمتغيرات الأخرى التي يجري تحديثها.

وشاهدوا هذا من منظور عاقل، إذا كنت تفترض أن البيان

وif(n != n)

وغير الذرية (التي أعتقد أنها معقولة، ولكن لا نعرف على وجه اليقين)، ثم باستثناء التأكيد لا يمكن أبدا أن يلقى.

هذا المثال يأتي في إطار "إشارة إلى الكائن الذي يحتوي على الحقل النهائية لم يهرب منشئ"

عند إنشاء مثيل جديد حامل كائن مع المشغل الجديد ،

  1. آلة جافا الافتراضية الأولى سيتم تخصيص (على الأقل) مساحة كافية على كومة من عقد كل المتغيرات سبيل المثال أعلن في حامل و superclasses.
  2. ثانيا الجهاز الظاهري سوف تهيئة جميع المتغيرات المثال الافتراضي القيم الأولية.3.ج الثالث, الجهاز الظاهري سيتم استدعاء الأسلوب في حامل الدرجة.

يرجى الرجوع عن أعلاه: http://www.artima.com/designtechniques/initializationP.html

نفترض:1 موضوع يبدأ الساعة 10:00 صباحا ، فإنه يدعو instatied حامل كائن قبل إجراء المكالمة من جديد هولير(42), 1) آلة جافا الافتراضية الأولى سيتم تخصيص (على الأقل) مساحة كافية على كومة من عقد كل المتغيرات سبيل المثال أعلن في حامل و superclasses.- سيكون 10:01 الوقت 2) الثانية, الجهاز الظاهري سوف تهيئة جميع المتغيرات المثال الافتراضي القيم الأولية -- سوف تبدأ الساعة 10:02 الوقت 3) الثالث, الجهاز الظاهري سيتم استدعاء الأسلوب في حامل الدرجة.-- سوف تبدأ الساعة 10:04 الوقت

الآن Thread2 بدأ --> 10:02:01 في الوقت ، وأنه سيتم إجراء مكالمة assertSanity() 10:03 في ذلك الوقت ن كان تهيئة الافتراضية صفر والثاني موضوع قراءة بيانات تالفة.

//غير آمنة المنشور العامة حامل حامل.

إذا كنت جعل الجمهور النهائي حامل حامل في حل هذه المشكلة

أو

خاصة int n;إذا قمت بإجراء خاص النهائي int n;سوف resole هذه المسألة.

يرجى الرجوع : http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html تحت قسم كيف النهائية مجالات العمل JMM ؟

وكنت أيضا في حيرة جدا من هذا المثال. لقد وجدت موقع يشرح الموضوع بشكل دقيق والقراء قد تجد من المفيد: HTTPS: // www.securecoding.cert.org/confluence/display/java/TSM03-J.+Do+not+publish+partially+initialized+objects

وتحرير: النص ذي الصلة من الرابط يقول:

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

وعلى JMM يسمح المجمعين تخصيص الذاكرة لمساعد جديد   وجوه وتعيين إشارة إلى أن الذاكرة إلى الميدان المساعد   قبل تهيئة كائن مساعد جديد. وبعبارة أخرى، فإن   مترجم يمكن إعادة ترتيب الكتابة إلى الميدان المثال مساعد و   إرسال التي تهيئة كائن مساعد (أي، this.n = ن) بحيث   السابق أسبق. هذا يمكن أن يعرض نافذة سباق خلالها   يمكن أن المواضيع الأخرى مراقبة كائن مساعد تهيئة جزئيا   المثال.

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