1 Ocak 2020 Çarşamba

ExecutorService Kapatma

Giriş
ExecutorService içindeki thread'ler kapatılmaz ise uygulama sonlanmıyor. Bu yüzden mutlaka kapatılmalı.

1. Thread'ler Sonlandığı İçin GC'ye Uğrarsa - Bu Tehlikeli Bir Yol
Tüm thread'leri biten bir ExecutorService, GC devreye girince kendiliğnden kapatılır. Açıklaması şöyle
"An unused ExecutorService should be shut down to allow reclamation of its resources."
ThreadPoolExecutor sınıfının finalize metodu şöyle.
/**
 * Invokes {@code shutdown} when this executor is no longer
 * referenced and it has no threads.
 */
protected void finalize() {
  shutdown();
}
Ancak buradaki koşul ExecutorService içindeki tüm thread'lerin sonlanmış olması. Açıklaması şöyle.
A pool that is no longer referenced in a program AND has no remaining threads will be shutdown automatically. If you would like to ensure that unreferenced pools are reclaimed even if users forget to call shutdown(), then you must arrange that unused threads eventually die, by setting appropriate keep-alive times, using a lower bound of zero core threads and/or setting allowCoreThreadTimeOut(boolean).
Açıklaması şöyle
The Worker threads that the executor creates are inner classes that have a reference back to the executor itself. (They need it to be able to see the queue, runstate, etc!) Running threads are not garbage collected, so with each Thread in the pool having that reference, they will keep the executor alive until all threads are dead. If you don't manually do something to stop the threads, they will keep running forever and your JVM will never shut down.
Örnek
Elimizde şöyle bir kod olsun. Bu thread'ler hiç bir zaman sonlanmayacağı için ExecutorService, GC ile kapanmaz.
ExecutorService executorService = Executors.newFixedThreadPool(3);
2. newSingleThreadExecutor
Bu özel bir Executor. Şöyle yaratılıyor. Yani bana aslında diğer metodlardan farklı olarak ThreadPoolExecutor yerine özel bir FinalizableDelegatedExecutorService döndürüyor. FinalizableDelegatedExecutorService nesnesi GC çalışınca içindeki ThreadPoolExecutor nesnesini kapatıyor.
public static ExecutorService newSingleThreadExecutor() {
  return new FinalizableDelegatedExecutorService(
    new ThreadPoolExecutor(1, 1,
                            0L, TimeUnit.MILLISECONDS,
                            new LinkedBlockingQueue<Runnable>())
    );
}

static class FinalizableDelegatedExecutorService extends DelegatedExecutorService {
  
  FinalizableDelegatedExecutorService(ExecutorService executor) {
    super(executor);
  }
  protected void finalize() {
    super.shutdown();
} }
3. try ile Kullanım - Yani Kodla Kapatma
ExecutorService iki tane shutdown() metoduna (shutdown() ve shutdownNow()) sahip. Java 19'a kadar maalesef şöyle kullanılamıyor. Java 19 ile artık böyle kullanılabiliyor.
try (service = Executors.newSingleThreadExecutor()) {
  // Add tasks to thread executor
  
} 
Şöyle yapabiliriz.
ExecutorService service = Executors.newSingleThreadExecutor();
try (Closeable close = service::shutdown) {

}
Daha klasik bir yöntem izlemek istersek şöyle yaparız.
ExecutorService service = null;
try {
  service = Executors.newSingleThreadExecutor();
  // Add tasks to thread executor
  
} finally {
  if(service != null) service.shutdown();
}
3. Guava
Guava'nın MoreExecutors sınıfının getExitingExecutorService() metodu kullanılırsa tüm thread'ler daemon thread haline getirilir ve JVM sonlanırken her şey kapatılır.

4. Kapatmayla İlgili Metodlar

awaitTermination metodu
İmzası şöyle.
boolean awaitTermination(long timeout, TimeUnit unit)
    throws InterruptedException;
Tüm işlerin bitinceye kadar bloke olur.
Örnek
Şöyle kullanılır.
ExecutorService executorService = Executors.newFixedThreadPool(10);
try {
  executorService.invokeAll(callables);
  executorService.shutdown();
  executorService.awaitTermination(1, TimeUnit.MINUTES);
} catch (InterruptedException e) {
  ...
}
isTerminated metodu
shutdown () metodu çağrıldıktan sonra tüm işler bitttiyse true döner.

isShutDown metodu
shutdown() metodu çağrılmışsa true döner.

shutdown metodu
Metodun imzası şöyle.
void shutdown();
Açıklaması şöyleÇalışmakta olan işlerin bitmesini bekler. Yeni iş kabul etmez. Bence shutdown() metodu, shutdownNow() metoduna tercih edilmeli. Çünkü çalışmakta olan işleri durdumaya zorlamaz ve bitmelerini bekler.
This method does not wait for previously submitted tasks to complete execution. Use awaitTermination to do that.
Örnek 
Şöyle yaparız. shutdown() metodundan sonra awaitTermination() çağrılıyor.
ExecutorService executor = ...

executor.shutdown();
if (!executor.awaitTermination(100, TimeUnit.MICROSECONDS)) {
  System.out.println("Still waiting after 100ms: calling System.exit(0)...");
  System.exit(0);
}
Örnek
Bitmemiş işleri görmek için şöyle yaparız.
executor.shutdown();
try {
  // define how much time to wait for the completion
  if (!executor.awaitTermination(15, TimeUnit.MINUTES)) { 
    List<Runnable> incompleteTask = executor.shutdownNow();
    // do that you want with them
  }
} catch (InterruptedException e) {
  // handle or log exception
}
shutdownNow metodu
Örnek
Şöyle yaparız. Bence bu metod shutdown varken kullanılmamalı.
ExecutorService e = Executors.newFixedThreadPool(3);
...
e.shutdownNow();

Örnek
Şöyle yaparız.
List<Runnable> incompleteTask = executor.shutdownNow();

Hiç yorum yok:

Yorum Gönder