سؤال

النظر في رمز قناص أدناه:

package sync;

public class LockQuestion {
    private String mutable;

    public synchronized void setMutable(String mutable) {
        this.mutable = mutable;
    }

    public String getMutable() {
        return mutable;
    }   
}

في Time Time1 Thread1 ، سيقوم Thread1 بتحديث متغير "قابل للتغيير". هناك حاجة إلى التزامن في Setter من أجل مسح الذاكرة من ذاكرة التخزين المؤقت المحلية إلى الذاكرة الرئيسية. في Time Time2 (time2> time1 ، لا يوجد مؤشر ترابط مؤشر ترابط مؤشر ترابط: سوف يقرأ قيمة قابلة للتغيير.

السؤال هو - هل أحتاج إلى وضع المزامنة قبل Getter؟ يبدو أن هذا لن يتسبب في أي مشكلات - يجب أن تكون الذاكرة محدثة ويجب إبطال ذاكرة ذاكرة التخزين المؤقت المحلية لـ Thread2 وتحديثها بواسطة Thread1 ، لكنني لست متأكدًا.

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

المحلول

بدلاً من أن أتساءل ، لماذا لا تستخدم فقط المراجع الذرية في java.util.concurrent?

(ولماذا يستحق ، قراءتي يحدث قبل لا يضمن أن يرى Thread2 تغييرات على قابلة للتغيير ما لم يستخدم أيضًا متزامنًا ... لكنني دائمًا ما أحصل على صداع من هذا الجزء من JLS ، لذلك استخدم المراجع الذرية)

نصائح أخرى

سيكون على ما يرام إذا قمت بتصنيع متقلبة قابلة للتغيير ، والتفاصيل في ""قفل القراءة والكتابة الرخيصة"

هل أنت متأكد تمامًا من أن الجائزة لن يتم استدعاؤها إلا بعد استدعاء Setter؟ إذا كان الأمر كذلك ، فأنت لا تحتاج إلى مزامنة getter ، لأن القراءات المتزامنة لا تحتاج إلى مزامنة.

إذا كانت هناك فرصة يمكن استدعاء Get and Set بشكل متزامن ، فأنت بالتأكيد بحاجة إلى مزامنة الاثنين.

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

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

أعتقد أنك يجب أن تبدأ بشيء صحيح وتحسينه لاحقًا عندما تعلم أن لديك مشكلة. أود فقط استخدام AtomicReference ما لم يكن هناك عدد قليل من النانو الثاني. ؛)

public static void main(String... args) {
    AtomicReference<String> ars = new AtomicReference<String>();
    ars.set("hello");
    long start = System.nanoTime();
    int runs = 1000* 1000 * 1000;
    int length = test(ars, runs);
    long time = System.nanoTime() - start;
    System.out.printf("get() costs " + 1000*time / runs + " ps.");
}

private static int test(AtomicReference<String> ars, int runs) {
    int len = 0;
    for (int i = 0; i < runs; i++)
        len = ars.get().length();
    return len;
}

مطبوعات

get() costs 1219 ps.

PS هو Pico-Second ، مع 1 مليون من الدقيقة الدقيقة.

ربما لن يؤدي هذا أبدًا إلى سلوك غير صحيح ، ولكن ما لم تضمن أيضًا الترتيب الذي تم تشغيله في مؤشرات الترابط ، لا يمكنك بالضرورة ضمان أن المترجم لم يعيد ترتيب القراءة في Thread2 قبل الكتابة في Thread1. اكثر تحديدا، يجب أن يضمن وقت تشغيل Java بأكمله فقط أن يتم تنفيذ المواضيع كما لو تم تشغيلها في المسلسل. لذلك ، طالما أن مؤشر الترابط له نفس الإخراج الذي يعمل بشكل متسلسل ضمن التحسينات ، فإن مكدس اللغة بأكمله (برنامج التحويل البرمجي ، والأجهزة ، ووقت تشغيل اللغة) يمكنه القيام بكل ما يريد. بما في ذلك السماح لرسائل ترابط 2 بتخزين نتيجة نتيجة LockQuestion.getMutable().

في الممارسة العملية ، سأكون مندهشًا جدًا إذا حدث ذلك. إذا كنت تريد أن تضمن أن هذا لا يحدث ، فلديك LockQuestion.mutable يتم الإعلان عنها final والحصول على تهيئة في المنشئ. أو استخدم المصطلح التالي:

private static class LazySomethingHolder {
  public static Something something = new Something();
}

public static Something getInstance() {
  return LazySomethingHolder.something;
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top