سؤال

يُبلغ Checkstyle عن هذا الرمز على أنه "تم كسر لغة القفل ذات التحقق المزدوج"، لكنني لا أعتقد أن الكود الخاص بي يتأثر فعليًا بمشاكل القفل الذي تم التحقق منه بشكل مزدوج.

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

الكود الزائف:

private void createRow(int id) {
  Row row = dao().fetch(id);
  if (row == null) {
     synchronized (TestClass.class) {
        row = dao().fetch(id);
        if (row == null) {
           dao().create(id);
        }
     }
  }
}

يمكنني أن أوافق على أنه يبدو وكأنه قفل تم التحقق منه مرتين، لكنني لا أستخدم متغيرات ثابتة وربما يكون الكود الموجود في fetch() و create() معقدًا جدًا بحيث لا يمكن تضمينه وإخراجه من الترتيب.

هل أنا مخطئ أم checkstyle؟:)

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

المحلول

وعلى افتراض انك تريد أن خط الأعمق لقراءة:

row = dao().create(id);

وانها ليست فحص مزدوج مشكلة قفل الكلاسيكية وmutexed افتراض dao().fetch بشكل صحيح من أسلوب إنشاء.

تعديل : في (تم تحديث الشفرة)

وهو وجود قيمة المخصصة المشكلة الكلاسيكية للقفل فحص مزدوج قبل حدوث التهيئة حيث اثنين من المواضيع يتم الوصول إلى نفس القيمة.

وعلى افتراض مزامنة DAO بشكل صحيح ولن يعود قيمة تمت تهيئته جزئيا، وهذا لا تعاني من عيوب فحص مزدوج لغة القفل.

نصائح أخرى

وأعتقد أنه في هذه الحالة، checkstyle غير صحيحة. في التعليمات البرمجية كما وردت، والنظر في ما يمكن أن يحدث إذا كان اثنان المواضيع على حد سواء قد row == null عند مدخل إلى كتلة متزامنة. سيكون موضوع ويدخل كتلة، وإدراج صف جديد. ثم بعد موضوع ومخارج كتلة، فإن موضوع B يدخل كتلة (لأنه لا يعرف ما حدث للتو)، ومحاولة إدراج نفس صف جديد مرة أخرى.

وأرى أنك مجرد تغيير الرمز وأضاف خط المفقود مهم جدا هناك. في الجديد كود، قد تكون قادرة على الابتعاد مع ذلك، منذ واثنين من المواضيع لا يمكن الاعتماد على التغييرات إلى متغير المشتركة (ثابت). ولكن قد يكون من الأفضل معرفة ما إذا يدعم DBMS الخاص بك عبارة مثل INSERT OR UPDATE.

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

إذا كنت تميل إلى كتابة تعليمات برمجية مثل هذا، ففكر في ما يلي:

  • منذ Java 1.4، أصبحت طرق المزامنة رخيصة جدًا.إنه ليس مجانيًا ولكن وقت التشغيل لا يعاني كثيرًا لدرجة أنه من المفيد المخاطرة بتلف البيانات.

  • منذ Java 1.5، لديك فئات Atomic* التي تسمح لك بقراءة الحقول وتعيينها بطريقة ذرية.لسوء الحظ، فإنهم لا يحلون مشكلتك.لماذا لم يضيفوا AtomicCachedReference أو أي شيء آخر (والذي من شأنه أن يستدعي طريقة قابلة للتجاوز عند استدعاء get() والقيمة الحالية == null) خارج عن ارادتي.

  • يحاول ehcache.يسمح لك بإعداد ذاكرة تخزين مؤقت (أيوالكائن الذي يسمح لك باستدعاء الكود إذا كان المفتاح موجودًا لا الواردة في الخريطة).هذا هو عادةً ما تريده وتقوم ذاكرات التخزين المؤقت بحل مشكلتك بالفعل (وجميع المشكلات الأخرى التي لم تكن تعلم بوجودها).

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

  1. كود Java غير متجمع (راجع إجابة @ Greg H)
  2. مرجع "الصف" هو فقط يتم التحقق من وجود قيمة خالية في السطر الأول، قبل كتلة المزامنة.

سبب كسر لغة القفل المزدوج (حسب القسم 16.2.4 من جافا التزامن في الممارسة العملية) هو أنه من الممكن أن يرى الخيط الذي يقوم بتشغيل هذه الطريقة قيمة غير فارغة لكن تمت تهيئته بشكل غير صحيح إشارة إلى "الصف"، قبل الدخول إلى الكتلة المتزامنة (ما لم يوفر "dao" المزامنة المناسبة).إذا كانت طريقتك تفعل أي شيء باستخدام "الصف" بخلاف التحقق من أنه فارغ أم لا، فسيتم كسره.كما هو الحال، ربما يكون الأمر على ما يرام ولكن هشة للغاية - شخصيًا، لن أكون مرتاحًا في الالتزام بهذا الرمز إذا اعتقدت أن هناك فرصة بعيدة لأن يقوم مطور آخر في وقت لاحق بتعديل الطريقة دون فهم التفاصيل الدقيقة لـ DCL.

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