في أقسام Java الهامة، ما الذي يجب أن أقوم بالمزامنة عليه؟

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

سؤال

في Java، الطريقة الاصطلاحية لإعلان الأقسام المهمة في الكود هي التالية:

private void doSomething() {
  // thread-safe code
  synchronized(this) {
    // thread-unsafe code
  }
  // thread-safe code
}

تتم مزامنة جميع الكتل تقريبًا this, ولكن هل هناك سبب معين لذلك؟هل هناك احتمالات أخرى؟هل هناك أي أفضل الممارسات بشأن الكائن الذي تريد المزامنة عليه؟(مثل الحالات الخاصة لـ Object?)

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

المحلول

أولاً، لاحظ أن مقتطفات التعليمات البرمجية التالية متطابقة.

public void foo() {
    synchronized (this) {
        // do something thread-safe
    }
}

و:

public synchronized void foo() {
    // do something thread-safe
}

يفعل بالضبط نفس الشيء.لا يوجد تفضيل لأي منهما باستثناء سهولة قراءة التعليمات البرمجية والأسلوب.

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

لاحظ أيضًا أن هناك مواقف قد ترغب فيها مزامنة من جانب العميل مجموعات من التعليمات البرمجية التي تطلب فيها الشاشة (على سبيل المثال:الكائن المتزامن) ليس بالضرورة this, ، كما في هذا المثال:

Vector v = getSomeGlobalVector();
synchronized (v) {
    // some thread-safe operation on the vector
}

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

نصائح أخرى

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

ومع ذلك، تنشأ حالة قبيحة بشكل خاص، إذا اخترت المزامنة على ملف java.lang.String.يمكن أن تكون السلاسل (وهذا ما يحدث دائمًا تقريبًا) داخليًا.وهذا يعني أن كل سلسلة ذات محتوى متساوٍ - في JVM بالكامل - تبين أنها نفس السلسلة خلف الكواليس.هذا يعني أنه إذا قمت بالمزامنة على أي سلسلة، فإن قسم التعليمات البرمجية الآخر (المختلف تمامًا) الذي يقوم أيضًا بتأمين سلسلة بنفس المحتوى، سوف يقوم بالفعل بقفل التعليمات البرمجية الخاصة بك أيضًا.

لقد كنت أستكشف ذات مرة مشكلة الجمود في نظام الإنتاج وإصلاحها وتتبعت حالة الجمود (بشكل مؤلم للغاية) إلى حزمتين مفتوحتين المصدر متباينتين تمامًا تمت مزامنتهما في مثيل String الذي كانت محتوياته على حد سواء "LOCK".

وأنا في محاولة لتجنب مزامنة على this لأن ذلك من شأنه أن يسمح الجميع من الخارج الذي كان إشارة إلى هذا الكائن لمنع تزامن بلدي. بدلا من ذلك، إنشاء كائن تزامن المحلي:

public class Foo {
    private final Object syncObject = new Object();
    …
}

والآن يمكنني استخدام هذا الكائن لمزامنة دون خوف من أي شخص "سرقة" القفل.

وفقط لتسليط الضوء على أن هناك أيضا ReadWriteLocks متاح في جاوة، كما وجدت java.util.concurrent.locks.ReadWriteLock.

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

في معظم الحالات، لا داعي للقلق حول مشكلات التوافق إذا كنت ببساطة فعل القراءة. إنه عندما تقومون به الكتابة التي كنت تقلق بشأن التحديثات المتزامنة (مما يؤدي إلى فقدان البيانات)، أو القراءة أثناء الكتابة (التحديثات الجزئية)، أن لديك ما يدعو للقلق.

وذلك لتأمين قراءة / الكتابة أكثر منطقية بالنسبة لي خلال متعددة الخيوط البرمجة.

وأنت تريد مزامنتها على كائن يمكن أن تكون بمثابة كائن المزامنة. إذا كان مثيل الحالي (<م> هذه مرجع) مناسبة (لا سينغلتون، على سبيل المثال)، قد استخدامها، كما هو الحال في جاوة أي كائن قد تكون بمثابة كائن المزامنة.

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

وهذا يعتمد كثيرا على البيئة التي نعمل فيها ونوع نظام كنت بناء. في معظم التطبيقات EE جافا رأيت، هناك فعلا حاجة حقيقية لتزامن ...

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

في الحالة السابقة، باستخدام this يسمح باستدعاء طرق متعددة بطريقة ذرية.أحد الأمثلة هو PrintWriter، حيث قد ترغب في إخراج أسطر متعددة (على سبيل المثال، تتبع المكدس إلى وحدة التحكم/المسجل) وضمان ظهورها معًا - في هذه الحالة، تعد حقيقة إخفاء كائن المزامنة داخليًا بمثابة ألم حقيقي.مثال آخر هو أغلفة المجموعة المتزامنة - حيث يجب عليك المزامنة على كائن المجموعة نفسه من أجل التكرار؛نظرًا لأن التكرار يتكون من استدعاءات متعددة للطرق لا تستطيع حمايته داخليا تماما.

في الحالة الأخيرة، أستخدم كائنًا عاديًا:

private Object mutex=new Object();

ومع ذلك، بعد أن رأيت العديد من عمليات تفريغ JVM وتتبعات المكدس التي تقول أن القفل هو "مثيل لـ java.lang.Object()" يجب أن أقول إن استخدام فئة داخلية قد يكون في كثير من الأحيان أكثر فائدة، كما اقترح آخرون.

على أية حال، هذا هو ما يستحق قطعتي.

يحرر:شيء آخر، عند المزامنة this أفضّل مزامنة الطرق، والحفاظ على الطرق دقيقة للغاية.أعتقد أن الأمر أكثر وضوحًا وإيجازًا.

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

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

وعلى أي حال، يتم استخدام الضمني (أسلوب متزامنة) أو synchronized(this) صريحة كثيرا، وأيضا في المكتبات جافا. إنها لغة جيدة، وإذا كان ذلك ممكنا، ينبغي أن يكون دائما خيارك الأول.

في ما مزامنة يعتمد على ما المواضيع الأخرى التي يحتمل أن ندخل في صراع مع هذه الدعوة الأسلوب يمكن مزامنة.

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

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

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

<اقتباس فقرة>   

وتقريبا جميع الكتل مزامنة على هذا، ولكن هناك سبب معين لذلك؟ هل هناك احتمالات أخرى؟

وهذا الإعلان بالتزامن طريقة بأكمله.

private synchronized void doSomething() {

وتزامن هذا الإعلان جزءا من كتلة التعليمات البرمجية بدلا من طريقة بأكمله.

private void doSomething() {
  // thread-safe code
  synchronized(this) {
    // thread-unsafe code
  }
  // thread-safe code
}

وثائق من أوراكل الصفحة

وجعل هذه الأساليب متزامنة تأثيرين:

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

<اقتباس فقرة>   

هل هناك احتمالات أخرى؟ هل هناك أي أفضل الممارسات بشأن ما الكائن لمزامنة على؟ (مثل حالات خاصة من كائن؟)

وهناك العديد من الاحتمالات والبدائل لالتزامن. يمكنك جعل موضوع التعليمات البرمجية آمنة باستخدام مستوى عال التزامن واجهات برمجة التطبيقات (متوفر منذ JDK 1.5 الإفراج)

Lock objects
Executors
Concurrent collections
Atomic variables
ThreadLocalRandom

والرجوع إلى أدناه الأسئلة SE لمزيد من التفاصيل:

التزامن مقابل قفل

تجنب متزامنة (هذا) في جافا؟

وأفضل الممارسات هو خلق كائن فقط لتوفير القفل:

private final Object lock = new Object();

private void doSomething() {
  // thread-safe code
  synchronized(lock) {
    // thread-unsafe code
  }
  // thread-safe code
}

ووبذلك أنت آمن، أن أي رمز الدعوة يمكن أن حالة توقف تام من أي وقت مضى الأسلوب الخاص بك عن طريق خط synchronized(yourObject) غير مقصود.

(<م> مشرفون علىjared و @يوفال آدم الذي شرح هذا في مزيد من التفاصيل أعلاه. )

وتخميني هو أن شعبية باستخدام this في الدروس جاءت من أوائل جافادوك الشمس: <لأ href = "https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html" يختلط = " noreferrer نوفولو "> https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html

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