FixedThreadPool vs CachedThreadPool: iki kötülükten daha azı


99

Bir dizi görevi yerine getiren iş parçacıkları (~ 5-150) oluşturan bir programım var. Başlangıçta a kullandım FixedThreadPoolçünkü bu benzer soru onların daha uzun ömürlü görevler için daha uygun olduğunu gösteriyordu ve çok iş parçacığına ilişkin çok sınırlı bilgimle, iş parçacığının ortalama ömrünü (birkaç dakika) " uzun ömürlü " olarak değerlendirdim.

Ancak, yakın zamanda ek iş parçacıkları oluşturma özelliğini ekledim ve bunu yapmak beni belirlediğim iş parçacığı sınırının üzerine çıkarıyor. Bu durumda, izin verebileceğim iş parçacığı sayısını tahmin etmek ve artırmak veya CachedThreadPoolboşa giden iş parçacığı kalmaması için a'ya geçmek daha mı iyi olur?

İkisini de başlangıçta denediğimde, bir fark yok gibi görünüyor , bu yüzden CachedThreadPoolisrafı önlemek için adaletle gitmeye meyilliyim . Bununla birlikte, iş parçacığının ömrü, bunun yerine bir seçmem FixedThreadPoolve kullanılmayan iş parçacıklarıyla ilgilenmem gerektiği anlamına mı geliyor? Bu soru , bu ekstra konuların boşa gitmediğini gösteriyor, ancak açıklamayı takdir ediyorum.

Yanıtlar:


113

Bir CachedThreadPool, uzun süre çalışan iş parçacıkları için bir tane kullanmanın olumsuz bir sonucu olmadığından, tam olarak durumunuz için kullanmanız gereken şeydir. Java belgesindeki CachedThreadPools'un kısa görevler için uygun olduğuna ilişkin yorum, yalnızca bu tür durumlar için özellikle uygun olduklarını, uzun süren görevleri içeren görevler için kullanılamayacaklarını veya kullanılmaması gerektiğini önermektedir.

Daha fazla ayrıntı için, Executors.newCachedThreadPool ve Executors.newFixedThreadPool , yalnızca farklı parametrelerle aynı iş parçacığı havuzu uygulamasıyla (en azından açık JDK'da) desteklenir. Farklılıklar sadece iş parçacığı minimum, maksimum, iş parçacığı öldürme süresi ve kuyruk tipidir.

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>());
}

Bir FixedThreadPool, aslında sabit sayıda iş parçacığı ile çalışmak istediğinizde kendi avantajlarına sahiptir, çünkü o zaman iş parçacığı sayısının belirttiğiniz düzeyde tutulacağını bilerek yürütücü hizmetine istediğiniz sayıda görev gönderebilirsiniz. İş parçacığı sayısını açıkça artırmak istiyorsanız, bu uygun bir seçim değildir.

Ancak bu, CachedThreadPool ile ilgili sahip olabileceğiniz tek sorunun, aynı anda çalışan iş parçacığı sayısını sınırlamakla ilgili olduğu anlamına gelir. CachedThreadPool onları sizin için sınırlamaz, bu nedenle çok fazla iş parçacığı çalıştırmadığınızdan emin olmak için kendi kodunuzu yazmanız gerekebilir. Bu gerçekten uygulamanızın tasarımına ve görevlerin yürütme hizmetine nasıl sunulduğuna bağlıdır.


2
"Bir CachedThreadPool tam olarak sizin durumunuz için kullanmanız gereken şeydir, çünkü uzun süre çalışan iş parçacıkları için bir tane kullanmanın olumsuz bir sonucu yoktur". Katılmadığımı düşünüyorum. CachedThreadPool, üst sınır olmaksızın dinamik olarak evreler oluşturur. Çok sayıda iş parçacığı üzerinde uzun süre çalışan görevler potansiyel olarak tüm kaynakları tüketebilir. Ayrıca, idealden daha fazla iş parçacığına sahip olmak, bu iş parçacıklarının bağlam değiştirilmesinde çok fazla kaynak israfına neden olabilir. Cevabın sonunda özel kısıtlamanın gerekli olduğunu açıklamış olsanız da, cevabın başlangıcı biraz yanıltıcıdır.
Nishit

1
Neden sadece sınırlı bir oluşturmak ThreadPoolExecutorgibi ThreadPoolExecutor(0, maximumPoolSize, 60L, TimeUnit.SECONDS, SynchronousQueue())?
Abhijit Sarkar

48

Hem FixedThreadPoolhem CachedThreadPoolde çok yüklü uygulamalarda kötüdür.

CachedThreadPool daha tehlikelidir FixedThreadPool

Uygulamanız çok yüklüyse ve düşük gecikme gerektiriyorsa, aşağıdaki dezavantajlar nedeniyle her iki seçenekten kurtulmak daha iyidir

  1. Görev kuyruğunun sınırsız yapısı: Hafıza yetersizliğine veya yüksek gecikmeye neden olabilir
  2. Uzun süre çalışan iş parçacığı CachedThreadPool, iş parçacığı oluşturmada kontrolden çıkmasına neden olur

Her ikisinin de kötü olduğunu bildiğiniz için, daha az kötülüğün hiçbir faydası yoktur. Birçok parametre üzerinde ayrıntılı kontrol sağlayan ThreadPoolExecutor'u tercih edin .

  1. Daha iyi kontrole sahip olmak için görev kuyruğunu sınırlı kuyruk olarak ayarlayın
  2. Doğru RejectionHandler'a Sahip Olun - Kendi Reddetme Yöneticiniz veya JDK tarafından sağlanan Varsayılan işleyicileriniz
  3. Görevin tamamlanmasından önce / sonra yapacak bir şeyiniz varsa, geçersiz kılın beforeExecute(Thread, Runnable)veafterExecute(Runnable, Throwable)
  4. İş parçacığı özelleştirmesi gerekiyorsa ThreadFactory'yi geçersiz kıl
  5. İş parçacığı havuzu boyutunu çalışma zamanında dinamik olarak kontrol edin (ilgili SE sorusu: Dinamik İş Parçacığı Havuzu )

Ya birisi CommonPool kullanmaya karar verirse?
Crosk Cool

1
@Ravindra - Hem CachedThreadPool hem de FixedThreadPool'un eksilerini güzelce açıkladınız. Bu, eşzamanlılık paketi hakkında derin bir anlayışa sahip olduğunuzu gösterir.
Ayaskant

5

Bu yüzden, bir dizi görevi yerine getiren iş parçacıkları (~ 5-150) oluşturan bir programım var.

İş parçacıklarının aslında işletim sisteminiz ve seçtiğiniz donanım tarafından nasıl işlendiğini anladığınızdan emin misiniz? Java iş parçacıklarını işletim sistemi iş parçacıklarına nasıl eşler, iş parçacıklarını CPU iş parçacıklarına nasıl eşler? Soruyorum çünkü ONE JRE'de 150 iş parçacığı oluşturmak, yalnızca altında büyük CPU çekirdeği / iş parçacığı varsa mantıklıdır, ki bu büyük olasılıkla böyle değildir. Kullanılan işletim sistemi ve RAM'e bağlı olarak, n'den fazla iş parçacığı oluşturmak JRE'nizin OOM hataları nedeniyle sonlandırılmasına bile neden olabilir. Öyleyse gerçekten iş parçacıkları ve bu iş parçacıkları tarafından yapılacak işler arasında gerçekten ayrım yapmalısınız, kaç işi işleyebildiğinizi vb.

Ve CachedThreadPool ile ilgili sorun da bu: Uzun süre çalışan işleri, aslında çalıştırılamayan iş parçacıklarında sıraya koymak mantıklı değil çünkü bu iş parçacıklarını işleyebilen yalnızca 2 CPU çekirdeğiniz var. 150 zamanlanmış iş parçacığı ile sonuçlanırsanız, Java ve işletim sistemi içinde bunları eşzamanlı olarak işlemek için kullanılan zamanlayıcılar için çok sayıda gereksiz ek yük yaratabilirsiniz. Yalnızca 2 CPU çekirdeğiniz varsa, iş parçacıklarınız her zaman G / Ç veya benzerlerini beklemiyorsa, bu imkansızdır. Ancak bu durumda bile çok sayıda iş parçacığı çok fazla I / O yaratır ...

Ve bu sorun, örneğin 2 + n iş parçacıklarıyla oluşturulan FixedThreadPool ile ortaya çıkmaz, burada n makul ölçüde düşüktür, çünkü bu donanım ve işletim sistemi kaynakları ile zaten çalışamayan iş parçacıkları yönetmek için çok daha az ek yük kullanılır.


Bazen daha iyi bir seçim olmayabilir, sadece 1 CPU çekirdeğiniz olabilir, ancak her kullanıcı isteğinin isteği işlemek için bir iş parçacığı tetikleyeceği bir sunucu çalıştırıyorsanız, özellikle planlıyorsanız başka makul bir seçenek olmayacaktır. kullanıcı tabanınızı genişlettikten sonra sunucuyu ölçeklendirmek.
Michel Feinstein

@mFeinstein Bir kişi bir iş parçacığı havuzu uygulamasını seçme konumunda iken nasıl bir seçeneğe sahip olamaz? Sizin 1 CPU çekirdeğine sahip örneğinizde, sadece daha fazla iş parçacığı üretmek mantıklı değil, bir FixedThreadPool kullanarak örneğime mükemmel bir şekilde uyuyor. Bu, önce bir veya iki çalışan iş parçacığı ile, daha sonra çekirdek sayısına bağlı olarak 10 veya 15 ile kolayca ölçeklenir.
Thorsten Schöning

2
Web sunucusu uygulamalarının büyük çoğunluğu, her yeni HTTP isteği için bir yeni iş parçacığı oluşturacaktır ... Makinenin kaç gerçek çekirdeğe sahip olduğunu umursamazlar, bu da uygulamayı daha basit ve ölçeklendirmeyi kolaylaştırır. Bu, yalnızca bir kez kodlamak ve dağıtmak istediğiniz ve makineyi değiştirirseniz yeniden derlemek ve yeniden konuşlandırmak zorunda kalmayacağınız birçok diğer tasarım için geçerlidir; bu, bir bulut örneği olabilir.
Michel Feinstein

@mFeinstein Çoğu web sunucusu, kendi istekleri için iş parçacığı havuzlarını kullanır, çünkü çalıştırılamayan iş parçacıkları bir anlam ifade etmez veya bağlantılar için olay döngülerini kullanır ve daha sonra havuzlardaki istekleri işler. Ek olarak, bir noktayı kaçırıyorsunuz, bu da sorunun doğru iş parçacığı havuzunu seçebilmesiyle ilgili olması ve yine de çalışamayan evrelerin üretilmesi hala bir anlam ifade etmiyor. Çekirdek ölçeklerine bağlı olarak makine başına makul miktarda iş parçacığı için yapılandırılmış bir FixedthreadPool gayet iyi.
Thorsten Schöning

3
@ ThorstenSchöning, 2 çekirdekli bir makinede 50 CPU'ya bağlı iş parçacığına sahip olmak yararsızdır. 2 çekirdekli bir makinede 50 GÇ'ye bağlı iş parçacığına sahip olmak çok yardımcı olabilir.
Paul Draper
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.