एक साधारण कार्यक्रम पर विचार करें:


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

प्रोग्राम चलाने से कंसोल आउटपुट उत्पन्न होता है जिसकी हम अपेक्षा करते हैं:

पूर्ण

लेकिन इसके बाद वह आउटपुट नहीं आता है जो हम आमतौर पर IntelliJ IDEA में देखते हैं:

प्रक्रिया निकास कोड 0 के साथ समाप्त हुई

हम आमतौर पर देखते हैं कि जब कोई कार्यक्रम समाप्त होता है।

ऐसा क्यों होता है?

NewFixedThreadPool () पद्धति का वर्णन हमें बताता है कि ExecutorService का उपयोग करके बनाए गए धागे तब तक मौजूद रहते हैं जब तक कि वे स्पष्ट रूप से बंद नहीं हो जाते। इसका मतलब यह है कि क्योंकि हमने ExecutorService को एक कार्य पारित किया है, इसे निष्पादित करने के लिए एक धागा बनाया गया था, और कार्य पूरा होने के बाद भी वह धागा मौजूद रहता है।

निष्पादक सेवा पर रोकना

नतीजतन, हमें निष्पादक सेवा को "बंद" (या बंद) करने की आवश्यकता है । हम इसे दो तरीकों से कर सकते हैं:

  1. शून्य शटडाउन () - इस विधि को कॉल करने के बाद, निष्पादक सेवा नई नौकरियों को स्वीकार करना बंद कर देती है। ExecutorService को पहले सबमिट किए गए सभी कार्य चलते रहेंगे।

    
    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. सूची <रननेबल> शटडाउननाउ () - यह विधि उन नौकरियों को रोकने का प्रयास करती है जो वर्तमान में सक्रिय हैं। कार्य जो अभी भी अपनी बारी की प्रतीक्षा कर रहे हैं उन्हें छोड़ दिया जाता है और रननेबल की सूची के रूप में वापस कर दिया जाता है ।

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

आउटपुट:

1
2
4
3
java.util.concurrent.FutureTask@1e80bfe8[पूरा नहीं हुआ, कार्य = java.util.concurrent.Executors$RunnableAdapter@4edde6e5[रैप्ड कार्य = परीक्षण$$Lambda$16/0x0000000800b95040@70177ecd]]
java.util.concurrent .FutureTask@cc34f4d[पूरा नहीं हुआ, टास्क = java.util.concurrent.Executors$RunnableAdapter@66a29884[रैप्ड टास्क = टेस्ट$$Lambda$16/0x0000000800b95040@4769b07b]]
java.util.concurrent.FutureTask@6f539caf[पूरा नहीं हुआ, टास्क = java.util.concurrent.Executors$RunnableAdapter@17a7cec2[रैप्ड टास्क = टेस्ट$$Lambda$16/0x0000000800b95040@65b3120a]]
5

निकास कोड 0 के साथ प्रक्रिया समाप्त

आउटपुट रन टू रन अलग होगा। आउटपुट में 2 प्रकार की लाइनें हैं:

  • एक संख्या का अर्थ है कि ExecutorService संबंधित कार्य को संसाधित करने में कामयाब रही, उस सूची से संख्या प्रदर्शित करते हुए जिसका उपयोग हमने कार्य बनाने के लिए किया था।

  • FutureTask ऑब्जेक्ट पर toString () विधि को कॉल करने के परिणाम । ये ऑब्जेक्ट वे कार्य हैं जो ExecutorService को सबमिट किए गए थे लेकिन संसाधित नहीं किए गए थे।

आउटपुट में एक और दिलचस्प बारीकियाँ हैं। एक आदर्श दुनिया में, हम पहले सभी प्रदर्शित संख्याएँ देखेंगे, उसके बाद FutureTask ऑब्जेक्ट। लेकिन तुल्यकालन के मुद्दे आउटपुट में लाइनों को उलझाते हैं।

अन्य तरीके

ExecutorService में इसे रोकने से संबंधित कई और तरीके हैं:

  1. बूलियन वेट टर्मिनेशन (लॉन्ग टाइमआउट, टाइमयूनीट यूनिट) - यह विधि उस थ्रेड को ब्लॉक करती है जो इसे कॉल करता है। निम्नलिखित तीन घटनाओं में से किसी एक के होते ही ब्लॉक समाप्त हो जाता है:

    • शटडाउन () विधि को कॉल करने के बाद , सभी सक्रिय कार्य और सभी निर्धारित कार्य निष्पादित किए गए हैं;
    • विधि मापदंडों द्वारा निर्धारित समय समाप्त हो गया है;
    • वह थ्रेड जिसे वेट टर्मिनेशन () विधि कहा जाता है, समाप्त हो गया है।

    यदि समय समाप्त होने से पहले 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"));
    	service.shutdown();
    	System.out.println(service.awaitTermination(1, TimeUnit.MICROSECONDS));
    }
    
  2. बूलियन isShutdown () - सही है अगर शटडाउन () या शटडाउन नाउ () विधि को निष्पादक सेवा पर बुलाया गया है ।

    
    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. बूलियन isTerminating () - सही है अगर शटडाउन () या शटडाउननाउ () विधि को ExecutorService पर कॉल किया गया है और सभी कार्य किए गए हैं।

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

उदाहरण कोड जो इन विधियों का उपयोग करता है:


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

आउटपुट (रन टू रन से अलग):

निष्पादन के लिए 10,000 कार्य प्रस्तुत किए गए थे।
9170 कार्य प्रारंभ नहीं हुए।
कुल कार्य पूर्ण: 830 कार्य।

प्रक्रिया निकास कोड 0 के साथ समाप्त हुई