في أي المواقف يمكن للكتلة المتزامنة الفارغة تحقيق دلالات الترابط الصحيحة؟

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

سؤال

كنت أبحث من خلال أ Findbugs تقرير عن قاعدة التعليمات البرمجية الخاصة بي وكان أحد الأنماط التي تم تشغيلها هو ملف فارغ synchronzied كتلة (أي. synchronized (var) {}).ال تقول الوثائق:

تعد الكتل المتزامنة الفارغة أكثر دقة ويصعب استخدامها بشكل صحيح مما يدركه معظم الناس ، كما أن الكتل المتزامنة الفارغة ليست أبدًا حلًا أفضل من الحلول الأقل مفتعلة.

في حالتي، حدث ذلك بسبب التعليق على محتويات الكتلة، ولكن synchronized وكان البيان لا يزال هناك.في أي الحالات يمكن أن تكون فارغة synchronized كتلة تحقيق دلالات الخيوط الصحيحة؟

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

المحلول

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

نصائح أخرى

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

int variable;

void test() // This code is INCORRECT
{
    new Thread( () ->  // A
    {
        variable = 9;
        for( ;; )
        {
            // Do other stuff
        }
    }).start();

    new Thread( () ->  // B
    {
        for( ;; )
        {
            if( variable == 9 ) System.exit( 0 );
        }
    }).start();
}

والبرنامج المذكور أعلاه غير صحيح. قد يكون مؤقتا قيمة المتغير محليا في موضوع A أو B أو كليهما. لذا B قد لا قراءة قيمة من 9 التي ويكتب، وربما بالتالي حلقة إلى الأبد.

إجراء تغيير متغير مرئية عبر المواضيع باستخدام كتل synchronized فارغة

واحد التصحيح الممكن هو إضافة volatile (على نحو فعال "لا مخبأ") معدل لمتغير. هذا في بعض الأحيان غير فعالة، ومع ذلك، لأنه يمنع تماما التخزين المؤقت للمتغير. كتل synchronized فارغة، من ناحية أخرى، لا يمنع التخزين المؤقت. كل ما يفعلونه هو إجبار مخابئ للمزامنة مع الذاكرة الرئيسية في بعض النقاط الحرجة. على سبيل المثال: *

int variable;

void test() // Corrected version
{
    new Thread( () ->  // A
    {
        variable = 9;
        synchronized( o ) {} // Flush to main memory
        for( ;; )
        {
            // Do other stuff
        }
    }).start();

    new Thread( () ->  // B
    {
        for( ;; )
        {
            synchronized( o ) {} // Refresh from main memory
            if( variable == 9 ) System.exit( 0 );
        }
    }).start();
}

final Object o = new Object();

كيف وضوح الضمانات نموذج الذاكرة

وكل المواضيع يجب مزامنة على نفس الكائن من أجل ضمان وضوح. هذا الضمان يقع على عاتق جافا نموذج الذاكرة ، ولا سيما على حكم أن "فتح العمل على رصد م <م> بالتزامن مع جميع الإجراءات اللاحقة قفل على م"، وبالتالي <م> يحدث قبل تلك أجراءات. لذلك لفتح مراقب س لفي ذيل كتلة synchronized لها يحدث قبل قفل B لاحقا على رأس كتلة لها. (ملاحظة، فمن هو هذا الغريب أجل الذيل رئيس العلاقة أن يفسر لماذا الهيئات يمكن أن يكون فارغا). ونظرا أيضا أن هناك في الكتابة تسبق فتح وقفل باء يسبق قراءة لها، يجب أن العلاقة تمتد لتشمل كلا من كتابة وقراءة: < م> الكتابة يحدث قبل قراءة . فمن هذا حاسمة بالنسبة مدد التي تجعل البرنامج المعدل الصحيح من حيث نموذج الذاكرة.

وأعتقد أن هذا هو أهم استخدام للكتل synchronized فارغة.


* <ط> أنا أتكلم كما لو كانت مسألة التخزين المؤقت المعالج لأنني أعتقد أن هذه طريقة مفيدة من النظر إليه. في الحقيقة، كما ألكسندر Dubinsky وقد علقت، "كل المعالجات الحديثة هي مخبأ متماسكة. ويحدث تسبق العلاقة هي أكثر ما يسمح للمترجم أن تفعل بدلا من وحدة المعالجة المركزية.

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

تقوم المزامنة بأكثر من مجرد الانتظار، في حين أن البرمجة غير الأنيقة يمكن أن تحقق التأثير المطلوب.

من http://www.javaperformancetuning.com/news/qotm030.shtml

  1. يكتسب الخيط القفل الموجود على الشاشة لهذا الكائن (بافتراض أن الشاشة غير مقفلة، وإلا فإن الخيط ينتظر حتى يتم إلغاء قفل الشاشة).
  2. تقوم ذاكرة الخيط بمسح كافة متغيراتها، أي.تتم قراءة جميع متغيراته بشكل فعال من الذاكرة "الرئيسية" (يمكن لـ JVMs استخدام مجموعات قذرة لتحسين ذلك بحيث يتم مسح المتغيرات "القذرة" فقط، ولكن هذا هو نفسه من الناحية النظرية.راجع القسم 17.9 من مواصفات لغة Java).
  3. يتم تنفيذ كتلة التعليمات البرمجية (في هذه الحالة يتم تعيين قيمة الإرجاع إلى القيمة الحالية لـ i3، والتي ربما تم إعادة تعيينها للتو من الذاكرة "الرئيسية").
  4. (عادةً ما يتم الآن كتابة أي تغييرات على المتغيرات إلى الذاكرة "الرئيسية"، ولكن بالنسبة لـ geti3() ليس لدينا أي تغييرات.)
  5. يقوم الخيط بتحرير القفل الموجود على الشاشة لكائن هذا.

لنظرة عميقة إلى نموذج الذاكرة جاوة، إلقاء نظرة على هذا الفيديو من "موضوعات متقدمة في لغات البرمجة" غوغل سلسلة: http://www.youtube.com/watch؟v=1FX4zco0ziY

وانه يعطي لمحة لطيفة حقا ما يمكن للمترجم (في كثير من الأحيان من الناحية النظرية، ولكن في بعض الأحيان من الناحية العملية) القيام به لالتعليمات البرمجية. الأشياء الأساسية لأي مبرمج جافا خطيرة!

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