سؤال

في Java ، يكون المهمة ذرية إذا كان حجم المتغير أقل من أو يساوي 32 بت ولكن ليس إذا كان أكثر من 32 بت.

ما (المتطاير/المتزامن) سيكون أكثر كفاءة في حالة التعيين المزدوج أو الطويل؟

مثل،

  volatile double x = y;

لا ينطبق متزامن مع الوسيطة البدائية. كيف يمكنني استخدام المزامنة في هذه الحالة؟ بالطبع لا أريد قفل صفي ، لذا this لا ينبغي أن تستخدم.

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

المحلول

إذا وجدت قفلًا على الكائن نفسه ثقيلًا جدًا ، فإن المزامنة هي الطريق للذهاب. قبل أن يكون Java 1.5 Folatile خيارًا جيدًا ، ولكن يمكن أن يكون للمتقلبة الآن تأثير كبير جدًا عن طريق فرض ترتيب التعليمات على الطريقة التي تحدث فيها المهمة. إنشاء كائن منفصل (private final Object X_LOCK = new Object();) وتزامنه عند الإعداد أو الحصول على قيمة ذلك المزدوج. سيعطيك هذا مستوى جيد من التحكم في القفل ، والذي يبدو أنك بحاجة.

في حزمة التزامن الجديدة ، هناك المزيد من الخيارات ، مثل AtomicReference التي قد تكون بديلاً جيدًا للمتقلب إذا كنت بحاجة حقًا إلى تجنب التزامن.

نصائح أخرى

ماذا تحاول أن تفعل؟ ال synchronized و volatile الكلمات الرئيسية هي آليات في Java والتي يمكن استخدامها لضمان ملاحظة القيم المتسقة من خلال مؤشرات ترابط مختلفة تقرأ نفس البيانات. على وجه الخصوص يسمحون لك بالتفكير يحدث قبل العلاقات في برامجك.

لا يمكنك ببساطة تجنب استخدام واحد من volatile أو synchronized من أجل الوصول بشكل صحيح غيرfinal الحقول في برنامج متعدد الخيوط. ومع ذلك ، السبب الرئيسي الذي يحتمل أن تتطلبه synchronized على volatile هو شرط استخدام الذرية قارن وتعيين العمليات (أي لن يكون أي اعتبار للأداء). على سبيل المثال ، في برنامج متعدد الخيوط:

volatile int i = 0;

public void foo() { 
    if (i == 0) i = i + 1;
}

الرمز أعلاه غير آمن بطبيعته ، على الرغم من أن إعلان المتغير باعتباره متقلبة يعني أن القراءات والكتابة يتم مسحها إلى الذاكرة الرئيسية - فإن التنفيذ الآمن الوحيد لمثل هذه الطريقة سيكون مثل:

int i = 0;

public synchronized void foo() {
    if (i == 0) i = i + 1;
}

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

يجدر أيضًا القول: أداء الأداء synchronized ليست مشكلة (في الغالبية العظمى من الحالات). عادةً ما ترجع مشكلات التزامن-الأداء إلى اختناقات الكود غير الضرورية ، أو الجمود أو الحدود ، ويمكن تخفيفها إذا لزم الأمر. أي دورة الساعة النقية سيتم تقويم النفقات العامة من قبل أشياء أخرى تقوم بتطبيقها: ملف IO ، استفسارات قاعدة البيانات ، عن بُعد وما إلى ذلك.

المتقلبة هي بالتأكيد الطريق للذهاب إذا كنت تقوم فقط بمهمة. أنا متأكد من أنك تعرف ، ولكن منذ أن نشأت: إذا كنت ترغب في القيام بعمليات أكثر تعقيدًا (زيادة القيمة على سبيل المثال) ستحتاج إلى مزامنة. I ++ ليس مؤشرًا آمنًا لأي نوع من أنواع المتغيرات. تحتاج إلى التزامن. I ++ وما شابه ذلك لأن هذا هو في الواقع أكثر من عملية واحدة.

لا: تم التعبير عن أنه يمكنك استخدام AtomicDouble ولكن لا يوجد حاليًا أي ذرة في java.util.concurrent.atomic

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

AtomicLong x = new AtomicLong(SomeValue);
public void doStuff() {
  double oldX;
  double newX;
  do {
    oldX = x.get();
    newX = calculateNewX(oldX);
  } while (!x.compareAndSet
      (Double.doubleToLongBits(oldX), Double.doubleToLongBits(newX)));

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

يمكنك بالطبع تنفيذ AtomicDouble بدلاً من إجراء تحويلات DoubleTolongBits هذه. ألق نظرة على Atomicfieldupdater.

Kdsrathore ، يمكنك استخدام بعض الأقفال الصريحة ، أو صنع بعض كائنات كائن وهمية = كائن جديد () ، والتي تتم مزامنتها في Setter/getter من ذلك المزدوج

بالنسبة الى وثائق أوراكل, ، يمكنك استخدام متقلبة للإشارة إلى كائن مزدوج:

volatile Double x = y;

"تكتب وقراءات المراجع دائمًا ذرية ، بغض النظر عما إذا كانت قد تم تنفيذها كقيم 32 بت أو 64 بت."

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