it-swarm.asia

FixedThreadPool vs CachedThreadPool: أهون الشرين

لذلك لدي برنامج يفرز الخيوط (~ 5-150) والتي تؤدي مجموعة من المهام. في الأصل كنت أستخدم FixedThreadPool لأن هذا السؤال المماثل اقترحوا أنهم كانوا أكثر ملاءمة لمهام أطول عملاً ومع معرفتي المحدودة للغاية في تعدد مؤشرات ترابط ، فكرت في متوسط ​​عمر سلاسل الرسائل (عدة دقائق) " عاشت مدة طويلة ".

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

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

82
Daniel

CachedThreadPool هو بالضبط ما يجب عليك استخدامه لموقفك حيث لا توجد أي نتائج سلبية لاستخدام واحد للخيوط طويلة المدى. يشير التعليق في doc Java حول كون CachedThreadPools مناسبًا لمهام قصيرة إلى أنها مناسبة بشكل خاص لمثل هذه الحالات ، وليس أنه لا يمكن استخدامها أو لا ينبغي استخدامها للمهام التي تنطوي على مهام طويلة المدى.

لمزيد من التوضيح ، Executors.newCachedThreadPool و Executors.newFixedThreadPool كلاهما مدعوم من نفس تطبيق تجمع مؤشرات الترابط (على الأقل في JDK المفتوحة) فقط مع معلمات مختلفة. الاختلافات فقط يجري الحد الأدنى لمؤشر الترابط الخاصة بهم ، الحد الأقصى ، وقت قتل مؤشر الترابط ، ونوع قائمة الانتظار.

public static ExecutorService newFixedThreadPool(int nThreads) {
     return new ThreadPoolExecutor(nThreads, nThreads,
                                   0L, TimeUnit.MILLISECONDS,
                                   new LinkedBlockingQueue<Runnable>());
 }

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                 60L, TimeUnit.SECONDS,
                                 new SynchronousQueue<Runnable>());
}

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

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

97
Trevor Freeman

كلا FixedThreadPool و CachedThreadPool شرور في التطبيقات المحملة بدرجة عالية.

CachedThreadPool أكثر خطورة من FixedThreadPool

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

  1. طبيعة غير محدودة لقائمة الانتظار المهمة: قد يسبب نفاد الذاكرة أو الكمون عالية
  2. ستتسبب مؤشرات الترابط الطويلة المدى CachedThreadPool في الخروج عن نطاق التحكم في إنشاء مؤشر الترابط

نظرًا لأنك تعلم أن كليهما شر ، فالشر الشرير لا ينفع. تفضيل ThreadPoolExecutor ، والذي يوفر التحكم الحبيبي في العديد من المعلمات.

  1. قم بتعيين قائمة انتظار المهام كقائمة انتظار محددة ليكون لها تحكم أفضل
  2. تمتع بحق RejectionHandler - معالجات RejectionHandler الخاصة بك أو الافتراضية التي يوفرها JDK
  3. إذا كان لديك شيء يجب القيام به قبل/بعد الانتهاء من المهمة ، فاستبدل beforeExecute(Thread, Runnable) و afterExecute(Runnable, Throwable)
  4. تجاوز ThreadFactory إذا كان تخصيص مؤشر الترابط مطلوبًا
  5. التحكم حجم تجمع مؤشر الترابط بشكل حيوي في وقت التشغيل (سؤال SE ذات الصلة: تجمع مؤشر الترابط الحيوي )
34
Ravindra babu

لذلك لدي برنامج يفرز الخيوط (~ 5-150) والتي تؤدي مجموعة من المهام.

هل أنت متأكد من أنك تفهم كيف تتم معالجة سلاسل الرسائل بالفعل بواسطة نظام التشغيل والأجهزة التي تختارها؟ كيف تقوم Java بتخطيط مؤشرات الترابط إلى مؤشرات نظام التشغيل ، وكيف تقوم بتخطيط مؤشرات الترابط إلى سلاسل عمليات وحدة المعالجة المركزية وما إلى ذلك؟ أنا أسأل لأن إنشاء 150 مؤشر ترابط في ONE JRE يكون منطقيًا فقط إذا كان لديك نوى/سلاسل عمليات CPU ضخمة تحتها ، وهذا على الأرجح ليس هو الحال. اعتمادًا على نظام التشغيل و RAM قيد الاستخدام ، قد يؤدي إنشاء أكثر من سلاسل رسائل إلى إنهاء JRE بسبب أخطاء OOM. لذلك يجب عليك التمييز بين سلاسل الرسائل والعمل الذي تقوم به من خلال هذه المواضيع ، وعدد العمل الذي يمكنك حتى معالجته وما إلى ذلك.

وتلك هي مشكلة CachedThreadPool: لا يعقل أن تصطف في طور العمل الطويل في سلاسل الرسائل التي لا يمكن تشغيلها بالفعل لأن لديك فقط 2 وحدة المعالجة المركزية النوى قادرة على معالجة هذه المواضيع. إذا انتهى بك الأمر إلى 150 موضوعًا مجدولة ، فقد تنشئ الكثير من النفقات العامة غير الضرورية للجداول المجدولة المستخدمة في Java ونظام التشغيل لمعالجتها بشكل متزامن. هذا مستحيل بكل بساطة إذا كان لديك فقط مركزان لوحدة المعالجة المركزية (CPU) ، إلا إذا كانت مؤشرات الترابط الخاصة بك في انتظار الإدخال/الإخراج أو طوال الوقت. ولكن حتى في هذه الحالة ، فإن الكثير من الخيوط من شأنه أن يخلق الكثير من I/O ...

ولا تحدث هذه المشكلة مع FixedThreadPool ، التي تم إنشاؤها باستخدام على سبيل المثال مؤشرات الترابط 2 + n ، حيث تكون n منخفضة بشكل معقول بطبيعة الحال ، لأنه مع استخدام هذا الجهاز وموارد نظام التشغيل يتم استخدام مقدار حمل أقل بكثير لإدارة مؤشرات الترابط التي لا يمكن تشغيلها على أي حال.

5
Thorsten Schöning