Basit bir program düşünün:


public static void main(String[] args) throws Exception {
	// Create an ExecutorService with a fixed number of threads: three
	ExecutorService service = Executors.newFixedThreadPool(3);
 
	// Pass a simple Runnable task to the ExecutorService
	service.submit(() -> System.out.println("done"));
}

Programı çalıştırmak, beklediğimiz konsol çıktısını üretir:

Tamamlandı

Ancak bunu genellikle IntelliJ IDEA'da gördüğümüz çıktı takip etmez:

İşlem, çıkış kodu 0 ile tamamlandı

Bunu genellikle bir program bittiğinde görürüz.

Bu neden oluyor?

newFixedThreadPool() yönteminin açıklaması bize, bir ExecutorService kullanılarak oluşturulan iş parçacıklarının açıkça durdurulana kadar var olmaya devam ettiğini söyler. Bu, ExecutorService'e bir görev ilettiğimiz için , onu yürütmek için bir iş parçacığının yaratıldığı ve bu iş parçacığının görev tamamlandıktan sonra bile var olmaya devam ettiği anlamına gelir.

ExecutorService'de Durdurma

Sonuç olarak, ExecutorService'i "kapatmamız" (veya durdurmamız) gerekir . Bunu iki şekilde yapabiliriz:

  1. void shutdown() — bu yöntem çağrıldıktan sonra ExecutorService yeni işleri kabul etmeyi durdurur. Daha önce ExecutorService'e gönderilen tüm görevler çalışmaya devam edecektir.

    
    public static void main(String[] args) throws Exception {
    ExecutorService service = Executors.newFixedThreadPool(3);
        	service.submit(() -> System.out.println("task 1"));
        	service.submit(() -> System.out.println("task 2"));
        	service.shutdown();
        	// A RejectedExecutionException will occur here
        	service.submit(() -> System.out.println("task 3"));
    }
    
  2. List<Runnable> shutdownNow() — Bu yöntem, o anda etkin olan işleri durdurmaya çalışır. Hâlâ sırasını bekleyen görevler atılır ve Runnables listesi olarak döndürülür .

    
    public static void main(String[] args) throws Exception {
        ExecutorService service = Executors.newFixedThreadPool(5);
        List.of(1, 2, 3, 4, 5, 6, 7, 8).forEach(i -> service.submit(() -> System.out.println(i)));
        List<Runnable> runnables = service.shutdownNow();
        runnables.forEach(System.out::println);
    }
    

Çıktı:

1
2
4
3
java.util.concurrent.FutureTask@1e80bfe8[Tamamlanmadı, görev = java.util.concurrent.Executors$RunnableAdapter@4edde6e5[Sarılmış görev = Test$$Lambda$16/0x0000000800b95040@70177ecd]]
java.util.concurrent .FutureTask@cc34f4d[Not completed, task = java.util.concurrent.Executors$RunnableAdapter@66a29884[Wrapped task = Test$$Lambda$16/0x0000000800b95040@4769b07b]]
java.util.concurrent.FutureTask@6f539caf[Not completed, task = java.util.concurrent.Executors$RunnableAdapter@17a7cec2[Sarılmış görev = Test$$Lambda$16/0x0000000800b95040@65b3120a]]
5

İşlem, çıkış kodu 0 ile tamamlandı

Çıktı çalıştırmadan çalıştırmaya farklılık gösterecektir. Çıktıda 2 tür satır vardır:

  • Bir sayı, ExecutorService'in karşılık gelen görevi işlemeyi başardığı ve görevleri oluşturmak için kullandığımız listeden numarayı gösterdiği anlamına gelir .

  • Bir FutureTask nesnesinde toString() yöntemini çağırmanın sonuçları . Bu nesneler, ExecutorService'e gönderilen ancak işlenmeyen görevlerdir.

Çıktının başka bir ilginç nüansı var. İdeal bir dünyada, önce görüntülenen tüm sayıları, ardından FutureTask nesnelerini görürdük . Ancak senkronizasyon sorunları, çıktıdaki satırları karıştırır.

Diğer yöntemler

ExecutorService'in onu durdurmakla ilgili birkaç yöntemi daha vardır:

  1. boolean waitTermination(long timeout, TimeUnit unit) — bu yöntem onu ​​çağıran iş parçacığını engeller. Engelleme, aşağıdaki üç olaydan herhangi biri gerçekleşir gerçekleşmez sona erer:

    • shutdown() yöntemi çağrıldıktan sonra , tüm etkin işler ve tüm programlanmış görevler yürütülür;
    • yöntem parametreleri tarafından belirlenen zaman aşımı süresi dolmuştur;
    • waitTermination() yöntemini çağıran iş parçacığı sonlandırılır.

    Yöntem, ExecutorService zaman aşımı süresi dolmadan önce durdurulursa true , zaman aşımı zaten geçmişse false değerini döndürür .

    
    public static void main(String[] args) throws Exception {
    	ExecutorService service = Executors.newFixedThreadPool(2);
    	service.submit(() -> System.out.println("task 1"));
    	service.submit(() -> System.out.println("task 2"));
    	service.submit(() -> System.out.println("task 3"));
    	service.shutdown();
    	System.out.println(service.awaitTermination(1, TimeUnit.MICROSECONDS));
    }
    
  2. boolean isShutdown()ExecutorService üzerinde shutdown () veya shutdownNow() yöntemi çağrılmışsa true değerini döndürür .

    
    public static void main(String[] args) throws Exception {
    	ExecutorService service = Executors.newFixedThreadPool(2);
    	service.submit(() -> System.out.println("task 1"));
    	service.submit(() -> System.out.println("task 2"));
    	service.submit(() -> System.out.println("task 3"));
    	System.out.println(service.isShutdown());
    	service.shutdown();
    	System.out.println(service.isShutdown());
    }
    
  3. boolean isTerished()ExecutorService üzerinde shutdown () veya shutdownNow() yöntemi çağrıldıysa ve tüm görevler tamamlandıysa true değerini döndürür.

    
    public static void main(String[] args) throws Exception {
        ExecutorService service = Executors.newFixedThreadPool(5);
        List.of(1, 2, 3, 4, 5, 6, 7, 8).forEach(i -> service.submit(() -> System.out.println(i)));
        service.shutdownNow();
        System.out.println(service.isTerminated());
    }
    

Bu yöntemleri kullanan örnek kod:


public static void main(String[] args) throws Exception {
   ExecutorService service = Executors.newFixedThreadPool(16);
   Callable<String> task = () -> {
       Thread.sleep(1);
       return "Done";
   };
 
   // Add 10,000 tasks to the queue
   List<Future<String>> futures = IntStream.range(0, 10_000)
           .mapToObj(i -> service.submit(task))
           .collect(Collectors.toList());
   System.out.printf("%d tasks were submitted for execution.%n", futures.size());
 
   // Attempt to shut down
   service.shutdown();
   // Wait 100 milliseconds to finish the work
   if (service.awaitTermination(100, TimeUnit.MILLISECONDS)) {
       System.out.println("All tasks completed!");
   } else {
       // Stop forcibly
       List<Runnable> notExecuted = service.shutdownNow();
       System.out.printf("%d tasks were not started.%n", notExecuted.size());
   }
 
   System.out.printf("Total tasks completed: %d.%n", futures.stream().filter(Future::isDone).count());
}

Çıktı (çalışmadan çalışmaya farklılık gösterir):

Yürütülmek üzere 10.000 görev gönderildi.
9170 görevleri başlatılmadı.
Tamamlanan toplam görev: 830 görev.

İşlem, çıkış kodu 0 ile tamamlandı