ExecutorService

2 4,663

ExecutorService, asenkron işler çalıştırmamızı kolaylaştıran JDK tarafından sunulan bir interface’dir. Bu interface aracılığı ile, thread seviyesinde ele almamız gereken işler ile (Thread oluşturma, hata sonucu sonlanan threadi tekrar çalıştırma vb) ilgilenmeden Thread havuzu oluştururuz.
ExecutorService örneği oluşturmanın iki farklı yöntemi vardır. Bunlardan ilki Executors sınıfında bulunan static factory metotlardır. İkincisi ise direk ThreadPoolExecutor sınıfının yapılandırıcısını kullanmaktır. Executors sınıfının metotlarına bakarsak, ThreadPoolExecutor yapılandırıcısının kullanıldığını görürürüz. ThreadPoolExecutor yapılandırıcısını kullanmak bize daha fazla esneklik sağlar. Her iki yöntemi incelemeye başlayalım.

Executors Sınıfının Static Metotları İle Tanımlama

1*Executors.newFixedThreadPool(int nThreads)

Dahilinde azami 10 thread barındıran bir thread havuzu ve thread havuzunu besleyen, yani çalıştırılması için gönderilen işleri tutan üst limiti olmayan bir queue oluşturulur. Static metotun koduna bakar isek, ThreadPoolExecutor yapılandırıcısını görürüz.

Bu metot aracılığı ile bir ExecutorService örneği oluşturuduğumuzda, threadler hemen yaratılmaz. İlk iş geldiğinde bir tane yaratılır. İkinci iş geldiğinde, eğer maksimum sayıya ulaşılmadıysa, boşta thread olsa bile yeni thread oluşturulur.

2*Executors.newCachedThreadPool();

Belirsiz sayılı bir thread havuzu oluşturur. Bir task geldiğinde yeni bir thread oluşturur ve bu thread 60 saniye boyunca havuzda kalır. Yeni bir iş geldiğinde, eğer boşta bir thread var ise, o kullanılır. Boşta thread yok ise yaratılır.

Bu metotun implementasyonuna bakar isek;

newFixedThreadPool da olduğu gibi ThreadPoolExecutor’ı görürüz.

3*Executors.newSingleThreadExecutor()

Tek bir thread oluşturur ve birim zamanda tek bir iş yürütür. İş esnasında hata oluştuğunda, yeni gelen iş için yeni bir thread oluşturur. Bu metot ile oluşturulan ExecutorService’in newFixedThreadExecutor(1) metotu sonucu oluşan örnekte farkı, konfigure edilememesidir. FinalizableDelegatedExecutorService’e dikkat.

Bu metotun implementasyonu;

ThreadPoolExecutor Sınıfının Yapılandırıcısı İle Tanımlama

Overload edilmiş bir kaç yapılandırıcı mevcuttur. İki tanesi üzerinden, ThreadPoolExecutor yapılandırıcısında kullanılan parametreleri inceleyelim.

corePoolSize: Havuzda tutulacak asgari thread sayısıdır. (allowCoreThreadTimeOut değeri set edilmediği sürece)
maximumPoolSize: Havuzdaki azami thread sayısını belirleyen parametre.
keepAliveTime: corePoolSize değerinin üzerindeki threadlerin ne kadar süre daha havuz içerisinde kalacağını belirleyen parametre.
unit: keepAliveTime değerinin birimini belirleyen parametre
workQueue: Taskların çalıştırılmadan önce tutuldukları queue’yu belirleyen parametre.
handler: Eğer üst sınırı olan bir workQueue ile çalışıyor ve bu queue kapasitesini doldurmuş ise, yeni gelen tasklar geri çevrilir . Handler parametresi ile RejectedExecutionHandler tipinde bir sınıf örneği veririz.

Örneğimizde, üst sınırı 1024 eleman olan bir queue kullanan threadPoolExecutor oluşturduk. Asgari 1 azami 5 thread olacak ve asgari sayı üzerindeki thread’ler 30 saniye sonunda bitirilecek.

ExecutorService’e İş Atama
ExecutorService execute, submit, invokeAny ve invokeAll isimli metotlar ile Runnable ve Callable örneklerini iş olarak alır ve çalıştırır.

execute: Execute metotu, Runnable tipinde bir iş alan ve geri dönüş değeri olmayan bir metottur. Atanan işin yapılıp yapılmadığı hakkında bir fikir vermez. Aşağıdaki gibi basit bir örnek ile execute metotunu kullanalım.

Bu basit örneğimizden sonra, ThreadPooExecutor yapılandırıcısı ile iki service oluşturalım. Ve bahsettiğimiz parametrelerin nasıl davrandığını inceleyelim.

MyRunnable isimli bir sınıf oluşturacağız. Bu sınıfımızdan oluşturacağımız işleri executor’a göndereceğiz. Ayrıca işlerin reject durumlarını işleyen RejectedExecutionHandler interface’ini implemente eden RejectedExecutionHandlerImp sınıfını oluşturacağız.

MyRunnable.java

RejectedExecutionHandlerImp.java

İlk ThreadPoolExecuter örneğimizi oluşturalım.

Servisimizin core thread sayısı 2, azami thread sayısı ise 4’dür. Kapasitesi 10 olan bir queue servisimiz tarafından kullanılacak. Kodumuzun çıktısı:

2 adet işimiz ilk etapta servis tarafından geri çevrildi. Çünkü queue’nun kapasitesi doldu. Ayrıca azami thread sayısına ulaştık.

Şimdi ikinci örneğimize bakalım. Bu örneğimizde, üst sınırı olmayan bir queue örneği kullandık.

Kodun çıktısı:

Üst limiti olmayan bir queue kullandığımızda, sadece coreThread sayısı kadar thread kullanıldı. Azami sayı kadar thread oluşturulmadı. Sanırım bu örnek ile parametreler daha anlaşılır olmuştur.

submit: Runnable ve Callable tiplerindeki işleri alır ve Future tipinde bir sonuç döner.

Runnable ile çalışan örneklerimizi yapalım. İlk örneğimiz;

submit(Runnable r, T result) metotu olacak. Future nesnesini döner. Metota geçilen ilk parametre çalıştırılacak kod parçasını içeren Runnable nesnesidir. İkincisi ise, geri dönüş değeridir. Atanan iş başarılı bir şekilde biterse, geri dönüş nesnesi olan Future’ın get metotu, bize parametre olarak geçtiğimiz değeri verir.

Kodun çıktısı aşağıdaki gibi olur. Result parametresi olarak Success gönderdik, iş başarı ile bittiğinden Future’ın get metotu Success değerini döndürdü.

İkinci örneğimiz ise;

submit(Runnable r) metotudur. Bu metotta geriye Future döner. Task başarılı ise, get metotu null döner.

Bir diğer overload submit metotu Callable tipinde iş alan metottur.

Kodun çıktısı:

invokeAll ve invokeAny, her iki metotta bir callable collection alır. InvokeAny, başarılı olan bir iş için result döner, invokeAll ise Future koleksiyonu ile tüm işler için sonuç döner.

Shutting Down

ExecutorService kendiliğinden bitirilmez. Aldığı işler bitse bile, yeni işleri bekler. ExecutorService’i kapatmak için shutdown ve shutdownNow metotlarını kullanırız.

Shutdown metotu, servisi hemen kapatmaz. İlk olarak yeni işleri almayı durdurur yürütülen işler bittiğinde de kapatır.

ShutdownNow metotu ise, hemen kapatır. O sırada çalışan işleri ise, bir liste halinde döner.

ExecutorService kapatma konusunda, önerilen yöntem ise, her iki metotu awaitTermination metotunuda ekleyerek beraber kullanmaktır.

İlk olarak yeni iş alımını engelledik. Sonrasında 5000 milisaniye servisin kapatılmasını bekliyoruz. Eğer kapatılmadı ise, shuwdownNow ile hemen kapatıyoruz.

İnşallah faydalı bir yazı olmuştur.

İyi Çalışmalar

2 Comments
  1. huseyin says

    Teşekkürler, emeğinize sağlık

  2. anonym says

    thnx dostumm, yardimci oldun

Email adresiniz yayınlanmayacaktır.