مساعدة في مراجعة التعليمات البرمجية التالية، هل هو خيط آمن؟

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

سؤال

private static Callback callback;

public Foo()
{
    super(getCallback());
}

private static Callback getCallback()
{
    callback = new Callback();
    return callback;
}

يمكن استدعاء منشئ foo () من خيوط متعددة. قلقي هو مع الحقل الثابت الخاص "رد الاتصال" والطريقة الثابتة "getcallback ()".

كما يمكن أن ينظر إليه، يتم استدعاء كل مرة "getcallback ()"، فإنه يعين قيمة جديدة إلى حقل ثابت "رد الاتصال".

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

يرجى تصحيح لي إذا كنت مخطئا. شكرا!

تحرير: نيتي هي الحفاظ على "رد الاتصال" في مكان ما في الفصل، لذلك يمكنني إعادة استخدامه لاحقا. ولكن هذا ليس بالأمر السهل لأن FOO تمتد من فئة تحتوي على إنشاء منشئ "اتصال".

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

المحلول

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

لكن لاحظ أنه ليس صحيح أن فقط static نتائج الكلمات الرئيسية في التعليمات البرمجية غير القيطات.

نصائح أخرى

انها ليست آمنة الموضوع. جرب هذه البدائل:

الخيار 1: هنا جميع الحالات تشارك نفس رد الاتصال

private static final Callback callback = new Callback();

public Foo() {
    super(callback);
}

الخيار 2: هنا كل مثيل لديه ردود اتصال الخاصة به

public Foo() {
    super(new Callback());
}

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

ستحصل رد الاتصال على قيمة جديدة كل مرة يتم استدعاء foo (حتى من نفس الخيط). لست متأكدا تماما ما يجب أن يقوم به رمزك (إذا كنت ترغب في تهيئة المتغير الثابت مرة واحدة فقط (Singleton)، يجب عليك التحقق مما إذا كان لا يزال فارغا في GetCallback () - وما هو ActionCallback؟). لجعله آمنة للخيط، استخدم متزامنة.

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

سؤال واحد واضح هو، يفعل callback يجب أن تكون ثابتة؟ أو هل يمكن أن تجعله بأمان حقل مثيل دون كسر وظيفة فصلك؟

أعلم أنه تم الرد عليه ولكن لماذا لم يتم تفصيلها حقا.

يتم استدعاء مؤشرات الترابطين طريقة GetCallback ()، يمكنهم تنفيذ الأسطر كما يلي:

  1. الموضوع 1 - رد الاتصال = اتصال جديد ()؛
  2. الموضوع 2 - Callback = اتصال جديد ()؛
  3. الموضوع 1 - العودة ActionCallback؛
  4. الموضوع 2 - العودة ActionCallback؛

في هذه الحالة، سيتم إرجاع رد الاتصال الناتج عند (2.) في كليهما (3.) و (4.)

يبدو أن الحل هو أن يسأل لماذا تحدد رد الاتصال بشكل ثابت إذا كانت خاصة بالمثيل وليس فئة.

أتمنى أن يساعد ذلك.

ما تحاول القيام به يسمى نمط Singleton، إذا قمت بالبحث، فيمكنك معرفة سبب وجود فكرة جيدة عموما لتجنب هذا النمط إذا استطعت، ولكن إذا كنت في حاجة إليها، يمكنك القيام بما يلي.

private static final Callback CALLBACK= new Callback();

أو إذا كنت بحاجة إلى Singleton كسول يمكنك القيام به

public class Foo {
   class CallbackHolder {
       static final Callback CALLBACK= new Callback();
   }

   public static Callback getCallback() {
      return CallbackHolder.CALLBACK;
   }

public Foo() {
    super(getCallback());
}

كلا التطبيقين آمنة للخيط.

هل تريد رد اتصال واحد لكل موضوع، واحد لكل كائن، أو singleton الحقيقي؟

بعض الرسومات حول كيفية القيام بالمتغيرات المختلفة - فقط من أعلى رأسي، لا تأخذ هذه حرفيا للغاية :)

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

واحد لكل موضوع:

  private static ThreadLocal<Callback> callback;

  public Foo()
  {
      super(getCallback());
  }

  private static Callback getCallback()
  {
      if ( callback.get() == null ) 
          callback.set(new Callback());
      return callback.get();
  }

رد رد واحد لجميع المواضيع:

  private final static Callback callback;

  static {
      callback = new Callback(); 
  }

  public Foo()
  {
      super(getCallback());
  }

  private static Callback getCallback()
  {
      return callback;
  }

ولإكمال، رد اتصال واحد لكل كائن:

  private Callback callback;

  public Foo()
  {
      super(getCallback());
  }

  private Callback getCallback()
  {
      callback = new Callback();
      return callback;
  }
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top