Isaalang-alang ang isang simpleng programa:


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

Ang pagpapatakbo ng programa ay gumagawa ng console output na inaasahan namin:

tapos na

Ngunit hindi ito sinusundan ng output na karaniwan nating nakikita sa IntelliJ IDEA:

Natapos ang proseso gamit ang exit code 0

Karaniwan nating nakikita iyon kapag natapos ang isang programa.

Bakit nangyayari iyon?

Ang paglalarawan ng newFixedThreadPool() na paraan ay nagsasabi sa amin na ang mga thread na ginawa gamit ang isang ExecutorService ay patuloy na umiiral hanggang sa tahasan ang mga ito na ihinto. Nangangahulugan iyon na dahil nagpasa kami ng isang gawain sa ExecutorService , isang thread ang ginawa upang isagawa ito, at ang thread na iyon ay patuloy na umiiral kahit na matapos ang gawain.

Huminto sa ExecutorService

Bilang resulta, kailangan nating "i-shut down" (o ihinto) ang ExecutorService . Magagawa natin ito sa dalawang paraan:

  1. void shutdown() — pagkatapos tawagin ang paraang ito, hihinto ang ExecutorService sa pagtanggap ng mga bagong trabaho. Ang lahat ng mga gawain na naunang naisumite sa ExecutorService ay patuloy na tatakbo.

    
    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() — Sinusubukan ng paraang ito na ihinto ang mga trabahong kasalukuyang aktibo. Ang mga gawain na naghihintay pa sa kanilang turn ay itatapon at ibinalik bilang isang listahan ng Runnables .

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

Output:

.
_
_
_
_
_ .FutureTask@cc34f4d[Hindi nakumpleto, gawain = java.util.concurrent.Executors$RunnableAdapter@66a29884[Balot na gawain = Pagsubok$$Lambda$16/0x0000000800b95040@4769b07b]kasabay na
95040@util9b07b.3future. gawain = java.util.concurrent.Executors$RunnableAdapter@17a7cec2[Balot na gawain = Pagsubok$$Lambda$16/0x0000000800b95040@65b3120a]]
5

Natapos ang proseso gamit ang exit code 0

Mag-iiba ang output mula sa run to run. Mayroong 2 uri ng mga linya sa output:

  • Ang isang numero ay nangangahulugan na ang ExecutorService ay pinamamahalaang iproseso ang kaukulang gawain, na ipinapakita ang numero mula sa listahan na ginamit namin upang lumikha ng mga gawain.

  • Ang mga resulta ng pagtawag sa toString() method sa isang FutureTask object. Ang mga bagay na ito ay ang mga gawain na isinumite sa ExecutorService ngunit hindi naproseso.

Ang output ay may isa pang kawili-wiling nuance. Sa isang perpektong mundo, makikita muna natin ang lahat ng ipinapakitang numero, na sinusundan ng mga bagay na FutureTask . Ngunit ang mga isyu sa pag-synchronize ay pinaghalo ang mga linya sa output.

Iba pang mga pamamaraan

Ang ExecutorService ay may ilang higit pang mga pamamaraan na nauugnay sa paghinto nito:

  1. boolean awaitTermination(long timeout, TimeUnit unit) — hinaharangan ng pamamaraang ito ang thread na tumatawag dito. Matatapos ang block sa sandaling mangyari ang alinman sa sumusunod na tatlong kaganapan:

    • pagkatapos tawagin ang pamamaraan ng shutdown() , lahat ng aktibong trabaho at lahat ng nakatakdang gawain ay naisakatuparan;
    • ang timeout na tinutukoy ng mga parameter ng pamamaraan ay lumipas na;
    • ang thread na tinatawag na awaitTermination() method ay winakasan.

    Ang pamamaraan ay nagbabalik ng true kung ang ExecutorService ay itinigil bago lumipas ang timeout, at false kung ang timeout ay lumipas na.

    
    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() — Nagbabalik ng true kung ang shutdown() o shutdownNow() na pamamaraan ay tinawag sa ExecutorService .

    
    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 isTerminated() — Nagbabalik ng true kung ang shutdown() o shutdownNow() na pamamaraan ay tinawag sa ExecutorService at lahat ng gawain ay tapos na.

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

Halimbawang code na gumagamit ng mga pamamaraang ito:


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

Output (naiiba sa run to run):

10,000 mga gawain ang isinumite para sa pagpapatupad.
9170 na gawain ang hindi nasimulan.
Kabuuang mga gawaing natapos: 830 mga gawain.

Natapos ang proseso gamit ang exit code 0