Başka bir iş parçacığı havuzu türü "önbelleğe alınmış" tır. Bu tür iş parçacığı havuzları, sabit olanlar kadar yaygın olarak kullanılır.

Adından da anlaşılacağı gibi, bu tür bir iş parçacığı havuzu, iş parçacıklarını önbelleğe alır. Yeni görevleri gerçekleştirmek için bu iş parçacıklarını yeniden kullanmak üzere kullanılmayan iş parçacıklarını sınırlı bir süre için canlı tutar. Böyle bir iş parçacığı havuzu, makul miktarda hafif işimiz olduğunda en iyisidir.

"Makul bir miktar" ifadesinin anlamı oldukça geniştir, ancak böyle bir havuzun her sayıda görev için uygun olmadığını bilmelisiniz. Örneğin, bir milyon görev oluşturmak istediğimizi varsayalım. Her biri çok az zaman alsa bile, yine de makul olmayan miktarda kaynak kullanır ve performansı düşürürüz. Yürütme süresi öngörülemez olduğunda, örneğin G/Ç görevlerinde bu tür havuzlardan da kaçınmalıyız.

Başlık altında, ThreadPoolExecutor yapıcısı aşağıdaki bağımsız değişkenlerle çağrılır:


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

Aşağıdaki değerler yapıcıya bağımsız değişken olarak iletilir:

Parametre Değer
corePoolSize ( yürütme hizmeti başladığında kaç iş parçacığının hazır olacağı (başlatılacağı) ) 0
maximumPoolSize (bir yürütme hizmetinin oluşturabileceği maksimum iş parçacığı sayısı ) Tamsayı.MAX_VALUE
keepAliveTime (iş parçacığı sayısı corePoolSize değerinden büyükse, serbest bırakılan bir iş parçacığının yok edilmeden önce yaşamaya devam edeceği süre ) 60 litre
birim (zaman birimleri) ZamanBirimi.SECONDS
workQueue (bir kuyruğun uygulanması) yeni SynchronousQueue<Runnable>()

Ve kendi ThreadFactory uygulamamızı tamamen aynı şekilde geçirebiliriz .

SynchronousQueue hakkında konuşalım

Eşzamanlı aktarımın temel fikri oldukça basittir ve yine de sezgisel değildir (yani, sezgiler veya sağduyu size bunun yanlış olduğunu söyler): bir kuyruğa bir öğe ekleyebilirsiniz, ancak ve ancak başka bir iş parçacığı öğeyi aynı anda alırsa aynı zamanda. Başka bir deyişle, senkronize bir kuyruğun içinde görevler olamaz, çünkü yeni bir görev gelir gelmez, yürüten iş parçacığı görevi çoktan almıştır .

Kuyruğa yeni bir görev girdiğinde, havuzda boş bir aktif iş parçacığı varsa, o zaman görevi alır. Tüm ileti dizileri meşgulse yeni bir ileti dizisi oluşturulur.

Önbelleğe alınmış bir havuz sıfır iş parçacığıyla başlar ve potansiyel olarak Integer.MAX_VALUE iş parçacığına kadar büyüyebilir . Temel olarak, önbelleğe alınmış bir iş parçacığı havuzunun boyutu yalnızca sistem kaynaklarıyla sınırlıdır.

Önbelleğe alınan iş parçacığı havuzları, sistem kaynaklarını korumak için bir dakika boşta kalan iş parçacıklarını kaldırır.

Pratikte nasıl çalıştığını görelim. Bir kullanıcı isteğini modelleyen bir görev sınıfı oluşturacağız:


public class Task implements Runnable {
   int taskNumber;

   public Task(int taskNumber) {
       this.taskNumber = taskNumber;
   }

   @Override
   public void run() {
       System.out.println("Processed user request #" + taskNumber + " on thread " + Thread.currentThread().getName());
   }
}
    

Ana yöntemde , newCachedThreadPool oluşturuyoruz ve ardından yürütme için 3 görev ekliyoruz. Burada hizmetimizin durumunu yazdırıyoruz (1) .

Ardından, 30 saniye duraklıyoruz, başka bir göreve başlıyoruz ve durumu gösteriyoruz (2) .

Bundan sonra ana iş parçacığımızı 70 saniye duraklatıyoruz, durumu yazdırıyoruz (3) , ardından tekrar 3 görev ekliyoruz ve durumu tekrar yazdırıyoruz (4) .

Görev ekledikten hemen sonra durumu gösterdiğimiz yerlerde, güncel çıktı için önce 1 saniye uyku ekliyoruz.


ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 3; i++) {
            executorService.submit(new Task(i));
        }
 
        TimeUnit.SECONDS.sleep(1);
            System.out.println(executorService);	//(1)
 
        TimeUnit.SECONDS.sleep(30);
 
        executorService.submit(new Task(3));
        TimeUnit.SECONDS.sleep(1);
            System.out.println(executorService);	//(2)
 
        TimeUnit.SECONDS.sleep(70);
 
            System.out.println(executorService);	//(3)
 
        for (int i = 4; i < 7; i++) {
            executorService.submit(new Task(i));
        }
 
        TimeUnit.SECONDS.sleep(1);
            System.out.println(executorService);	//(4)
        executorService.shutdown();

Ve işte sonuç:

havuz-1-thread-1 iş parçacığında işlenen kullanıcı isteği #0
havuz-1-thread-2 iş parçacığında işlenen kullanıcı isteği
#1 havuz-1-thread-3 iş parçacığında işlenen kullanıcı isteği #
2 (1) java.util.concurrent .ThreadPoolExecutor@f6f4d33[Çalışıyor, havuz boyutu = 3, aktif iş parçacıkları = 0, sıradaki görevler = 0, tamamlanan görevler = 3] Havuz-
1-iş parçacığı-2 iş parçacığında işlenen kullanıcı isteği #3 (
2) java.util.concurrent. ThreadPoolExecutor@f6f4d33[Çalışıyor, havuz boyutu = 3, aktif iş parçacıkları = 0, sıradaki görevler = 0, tamamlanan görevler = 4] (3) java.util.concurrent.ThreadPoolExecutor@f6f4d33[Çalışıyor, havuz
boyutu = 0, etkin iş parçacıkları = 0 , sıraya alınmış görevler = 0, tamamlanan görevler = 4]
Pool-1-thread-4 iş parçacığında işlenen kullanıcı isteği #4
havuz-1-thread-5 iş parçacığında işlenen kullanıcı isteği #5
Havuz-1-thread-4 iş parçacığında işlenen kullanıcı isteği #6
(4) java.util.concurrent.ThreadPoolExecutor@f6f4d33[Çalışıyor, havuz boyutu = 2, etkin iş parçacıkları = 0, sıraya alınmış görevler = 0, tamamlanmış görevler = 7]

Adımların her birini gözden geçirelim:

Adım Açıklama
1 (tamamlanan 3 görevden sonra) 3 thread oluşturduk ve bu 3 thread üzerinde 3 görev yürütüldü.
Durum görüntülendiğinde, 3 görevin tümü yapılır ve iş parçacıkları diğer görevleri gerçekleştirmeye hazırdır.
2 (30 saniyelik duraklamadan ve başka bir görevin yürütülmesinden sonra) 30 saniye işlem yapılmadığında, ileti dizileri hala canlıdır ve görevleri bekler.
Kalan canlı iş parçacıklarının havuzundan alınan bir iş parçacığına başka bir görev eklenir ve yürütülür.
Havuza yeni ileti dizisi eklenmedi.
3 (70 saniyelik duraklamadan sonra) Konular havuzdan kaldırıldı.
Görevleri kabul etmeye hazır ileti dizisi yok.
4 (3 görev daha yürüttükten sonra) Daha fazla görev alındıktan sonra yeni ileti dizileri oluşturuldu. Bu sefer sadece iki iş parçacığı 3 görevi işlemeyi başardı.

Pekala, artık başka tür bir yürütücü hizmetinin mantığını öğrendiniz.

Executors yardımcı program sınıfının diğer yöntemlerine benzer şekilde , newCachedThreadPool yöntemi ayrıca bir ThreadFactory nesnesini bağımsız değişken olarak alan aşırı yüklenmiş bir sürüme sahiptir .