سؤال

قل لديك الطبقة التالية

public class AccessStatistics {
  private final int noPages, noErrors;
  public AccessStatistics(int noPages, int noErrors) {
    this.noPages = noPages;
    this.noErrors = noErrors;
  }
  public int getNoPages() { return noPages; }
  public int getNoErrors() { return noErrors; }
}

وتنفذ التعليمات البرمجية التالية

private AtomicReference<AccessStatistics> stats =
  new AtomicReference<AccessStatistics>(new AccessStatistics(0, 0));

public void incrementPageCount(boolean wasError) {
  AccessStatistics prev, newValue;
  do {
    prev = stats.get();
    int noPages = prev.getNoPages() + 1;
    int noErrors = prev.getNoErrors;
    if (wasError) {
      noErrors++;
    }
    newValue = new AccessStatistics(noPages, noErrors);
  } while (!stats.compareAndSet(prev, newValue));
}

في السطر الأخير while (!stats.compareAndSet(prev, newValue)) كيف يمكن لل compareAndSet طريقة تحديد المساواة ما بين prev و newValueب هل AccessStatistics الطبقة المطلوبة لتنفيذ equals() طريقة؟ إذا لم يكن كذلك، لماذا؟ ينص جافادوك ما يلي AtomicReference.compareAndSet

يضبط القيمة بشكل غير رسمي القيمة المحدثة المحددة إذا كانت القيمة الحالية == القيمة المتوقعة.

... ولكن يبدو أن هذا التأكيد عام للغاية والبرامج التعليمية التي قرأتها على atomicReareferver لا أقترح أبدا تنفيذ تساوي () لفئة ملفوفة في AtomiCareference.

إذا كانت الفئات ملفوفة في AtomicReareference مطلوبة لتنفيذ المساواة ()، ثم بالنسبة للكائنات أكثر تعقيدا من AccessStatistics أعتقد أنه قد يكون أسرع لمزامنة الأساليب التي تحديث الكائن وعدم استخدام AtomIcReReference.

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

المحلول

يقارن التعامل تماما كما لو كنت قد استخدمت المشغل ==. وهذا يعني أن المراجع يجب أن تشير إلى نفس المثيل. الكائنات. لا يستخدم المساكن ().

نصائح أخرى

في الواقع، ليس قارن Prev and newvalue!

بدلا من ذلك، يقارن القيمة المخزنة داخل الإحصائيات إلى السابق وفقط عندما تكون تلك هي نفسها، فإنها تقوم بتحديث القيمة المخزنة داخل الإحصائيات إلى NewValue. كما قال أعلاه يستخدم المشغل المساواة (==) للقيام بذلك. هذا يعني أنه عندما يكون Prev يشير إلى نفس الكائن كما يتم تخزينه في الإحصائيات سيتم تحديث الإحصائيات.

يبحث ببساطة عن المساواة المرجعية للكائن (AKA ==)، لذلك إذا تم تغيير مرجع الكائن الذي يحتفظ به AtomicReSReference بعد المرجع، فلن يغير المرجع، لذلك عليك أن تبدأ من جديد.

فيما يلي بعض من قواعد المصدر في المصدر. يشير AtomicReReferfer إلى مرجع كائن. هذا المرجع هو متغير عضو مضطرب في مثيل AtomicReference على النحو التالي.

private volatile V value;

احصل فقط على إرجاع أحدث قيمة المتغير (كما تفعل التقلب في "يحدث قبل" من قبل ".

public final V get()

فيما يلي أهم طريقة للطاقة الذرية.

public final boolean  compareAndSet(V expect, V update) {
        return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
}

تتم استدعاء الأسلوب ClareNeAndset (توقع وتحديث) طريقة الفئة غير الآمنة من Java. تستثمر طريقة الاتصال غير آمنة للمكالمات الأصلية، والتي تستثمر تعليمات واحدة إلى المعالج. "نتوقع" و "تحديث" كل إشارة كائن.

إذا لم يتم الإشارة إلى القيمة المتغيرة "القيمة" مثيل AtomiCrearefer "إلى نفس الكائن حسب" توقع "، يتم تعيين" UPDATE "إلى متغير هذا المثيل الآن، ويتم إرجاع" True ". أو آخر، يتم إرجاع خطأ. كل شيء يتم بطريقة بحرية. لا يوجد موضوع آخر يمكن اعتراض بينهما. نظرا لأن هذا عملية معالج واحد (سحر بنية الكمبيوتر الحديثة)، فغالبا ما يكون أسرع من استخدام كتلة متزامنة. لكن تذكر أنه عند تحديث المتغيرات المتعددة بشكل جذري، لن يساعد AtomicRefervarefer.

أرغب في إضافة رمز قيد التشغيل الكامل، والذي يمكن تشغيله في الكسوف. سوف يوضح العديد من الارتباك. هنا يحاول 22 مستخدما (خيوط الأسطورة) حجز 20 مقعدا. فيما يلي مقتطف التعليمات البرمجية متبوعا برمز كامل.

مقتطف الكود حيث يحاول 22 مستخدما حجز 20 مقعدا.

for (int i = 0; i < 20; i++) {// 20 seats
            seats.add(new AtomicReference<Integer>());
        }
        Thread[] ths = new Thread[22];// 22 users
        for (int i = 0; i < ths.length; i++) {
            ths[i] = new MyTh(seats, i);
            ths[i].start();
        }

فيما يلي رابط GitHub لأولئك الذين يريدون رؤية رمز التشغيل الكامل وهو صغير وموجز.https://github.com/sankar4git/atomicreence/blob/master/solution.java.

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