كيف يمكنك التأكد من أن سلاسل الرسائل المتعددة يمكنها الوصول بأمان إلى حقل الفصل؟

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

سؤال

عندما يتم الوصول إلى حقل فئة عبر طريقة getter بواسطة سلاسل رسائل متعددة، كيف يمكنك الحفاظ على سلامة سلسلة الرسائل؟هل الكلمة الأساسية المتزامنة كافية؟

هل هذا آمن:

public class SomeClass {
    private int val;

    public synchronized int getVal() {
        return val;
    }

    private void setVal(int val) {
        this.val = val;
    }
}

أم أن الواضع يقدم المزيد من التعقيدات؟

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

المحلول

إذا كنت تستخدم 'synchronized' في جهاز الضبط هنا أيضًا، فهذا الرمز آمن للخيط.ومع ذلك، قد لا تكون دقيقة بما فيه الكفاية؛إذا كان لديك 20 حرفًا وحرفًا وكلها متزامنة، فمن المحتمل أنك تقوم بإنشاء عنق الزجاجة في المزامنة.

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

 int old = someThing.getVal();
 if (old == 1) {
    someThing.setVal(2);
 }

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

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

نصائح أخرى

بالإضافة إلى تعليق كوان, ، يمكنك القيام بما يلي للمقارنة والتخزين:

synchronized(someThing) {
    int old = someThing.getVal();
    if (old == 1) {
        someThing.setVal(2);
    }
}

يعمل هذا لأن القفل المحدد عبر طريقة متزامنة هو ضمنيًا نفس قفل الكائن (راجع مواصفات لغة جافا).

من وجهة نظري، يجب عليك استخدام المزامنة في كل من طريقتي getter وsetter، وهذا يكفي.

يحرر:هنا أ وصلة لمزيد من المعلومات حول المزامنة وما لا.

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

public class ThreadSafeSomeClass {

    private final AtomicInteger value = new AtomicInteger(0);

    public void setValue(int x){
         value.set(x);
    }

    public int getValue(){
         return value.get();
    }

}

ومع ذلك، إذا قمت بإضافة متغيرات إضافية بحيث تكون تابعة (حالة متغير واحد تعتمد على حالة متغير آخر)، فلن يعمل AtomicInteger.

تكرار الاقتراح بقراءة "Java Concurrency in Practice".

بالنسبة للأشياء البسيطة قد يكون هذا كافيا.في معظم الحالات، يجب عليك تجنب الكلمة الأساسية المتزامنة لأنك قد تواجه طريقًا مسدودًا للمزامنة.

مثال:

public class SomeClass {

  private Object mutex = new Object();
  private int    val   = -1; // TODO: Adjust initialization to a reasonable start
                             //       value
  public int getVal() {
    synchronized ( mutex ) {
      return val;
    }
  }

  private void setVal( int val ) {
    synchronized ( mutex ) {
      this.val = val;
    }
  }
}

التأكد من أن مؤشر ترابط واحد فقط يقرأ أو يكتب إلى عضو المثيل المحلي.

اقرأ كتاب "البرمجة المتزامنة في Java(tm):"مبادئ وأنماط التصميم (جافا (أديسون ويسلي))"، ربما http://java.sun.com/docs/books/tutorial/essential/concurrency/index.html مفيد أيضاً...

المزامنة موجودة للحماية من تداخل الخيوط وأخطاء تناسق الذاكرة.من خلال المزامنة على getVal()، يضمن الكود عدم تنفيذ الطرق المتزامنة الأخرى على SomeClass أيضًا في نفس الوقت.نظرًا لعدم وجود طرق متزامنة أخرى، فهي لا تقدم قيمة كبيرة.لاحظ أيضًا أن عمليات القراءة والكتابة على العناصر الأولية لها وصول ذري.وهذا يعني أنه مع البرمجة الدقيقة، لا يحتاج المرء إلى مزامنة الوصول إلى الحقل.

يقرأ المزامنة.

لست متأكدًا حقًا من سبب انخفاض هذا إلى -3.أنا ببساطة ألخص ما يقوله البرنامج التعليمي للمزامنة من Sun (بالإضافة إلى تجربتي الخاصة).

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

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