आपको एक्ज़ीक्यूटर इंटरफ़ेस की आवश्यकता क्यों है?

जावा 5 से पहले, आपको अपने आवेदन में अपना खुद का कोड थ्रेड प्रबंधन लिखना था। इसके अलावा, एक बनानानया सूत्रऑब्जेक्ट एक संसाधन-गहन ऑपरेशन है, और हर हल्के कार्य के लिए एक नया धागा बनाने का कोई मतलब नहीं है। और क्योंकि यह समस्या बहु-थ्रेडेड एप्लिकेशन के हर डेवलपर से परिचित है, इसलिए उन्होंने इस कार्यक्षमता को एक्ज़ीक्यूटर फ्रेमवर्क के रूप में जावा में लाने का फैसला किया।

बड़ा विचार क्या है? यह सरल है: प्रत्येक नए कार्य के लिए एक नया थ्रेड बनाने के बजाय, थ्रेड्स को एक प्रकार के "स्टोरेज" में रखा जाता है, और जब कोई नया कार्य आता है, तो हम एक नया बनाने के बजाय मौजूदा थ्रेड को पुनः प्राप्त करते हैं।

इस ढांचे के मुख्य इंटरफेस एक्ज़ीक्यूटर , एक्ज़ीक्यूटर सर्विस और शेड्यूल्ड एक्ज़ीक्यूटर सर्विस हैं , जिनमें से प्रत्येक पिछले एक की कार्यक्षमता का विस्तार करता है।

एक्ज़ीक्यूटर इंटरफ़ेस बेस इंटरफ़ेस है। यह एक एकल शून्य निष्पादन (रननेबल कमांड) विधि की घोषणा करता है जिसे रननेबल ऑब्जेक्ट द्वारा कार्यान्वित किया जाता है।

ExecutorService इंटरफ़ेस अधिक दिलचस्प है इसमें काम पूरा होने के प्रबंधन के तरीके हैं, साथ ही किसी प्रकार के परिणाम वापस करने के तरीके भी हैं। आइए इसके तरीकों पर करीब से नज़र डालें:

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

इस विधि को कॉल करने से निष्पादक सेवा बंद हो जाती है । प्रसंस्करण के लिए पहले से ही प्रस्तुत किए गए सभी कार्यों के लिए थ्रेड.इंटरप्ट को बुलाया जाएगा। यह विधि कतारबद्ध कार्यों की सूची लौटाती है।

विधि कॉल के समय "प्रगति में" होने वाले सभी कार्यों के पूरा होने की प्रतीक्षा नहीं करती है।

चेतावनी: इस पद्धति को कॉल करने से संसाधनों का रिसाव हो सकता है।

बूलियन शटडाउन है (); जाँचता है कि क्या निष्पादक सेवा बंद कर दी गई है।
बूलियन समाप्त हो गया है (); निष्पादक सेवा के बंद होने के बाद सभी कार्य पूर्ण होने पर सत्य वापस आ जाता है । जब तक शटडाउन () या शटडाउन नाऊ () कहा जाता है, यह हमेशा गलत रिटर्न देगा ।
बूलियन वेट टर्मिनेशन (लॉन्ग टाइमआउट, टाइमयूनीट यूनिट) इंटरप्टेड एक्सेप्शन फेंकता है;

शटडाउन () विधि को कॉल करने के बाद , यह विधि उस थ्रेड को ब्लॉक कर देती है जिस पर इसे कहा जाता है, जब तक कि निम्न स्थितियों में से कोई एक सत्य न हो:

  • सभी निर्धारित कार्य पूर्ण हैं;
  • विधि को दिया गया समय समाप्त हो गया है;
  • वर्तमान धागा बाधित है।

यदि सभी कार्य पूर्ण हो जाते हैं, तो सत्य वापस आ जाता है , और समाप्ति से पहले समय समाप्त होने पर गलत हो जाता है।

<टी> भविष्य <टी> सबमिट करें (कॉल करने योग्य <टी> कार्य);

निष्पादक सेवा में एक कॉल करने योग्य कार्य जोड़ता है और भविष्य के इंटरफ़ेस को लागू करने वाली वस्तु देता है ।

<T> पारित कार्य के परिणाम का प्रकार है।

<टी> भविष्य <टी> सबमिट करें (चलाने योग्य कार्य, टी परिणाम);

ExecutorService में एक रन करने योग्य कार्य जोड़ता है और भविष्य के इंटरफ़ेस को लागू करने वाली वस्तु देता है ।

T परिणाम पैरामीटर वह है जो परिणामी पर प्राप्त () विधि को कॉल करके लौटाया जाता हैभविष्य वस्तु।

भविष्य <?> सबमिट करें (चलाने योग्य कार्य);

ExecutorService में एक रन करने योग्य कार्य जोड़ता है और भविष्य के इंटरफ़ेस को लागू करने वाली वस्तु देता है ।

यदि हम भविष्य की वस्तु पर प्राप्त () विधि को कॉल करते हैं , तो हमें शून्य मिलता है।

<टी> सूची <भविष्य <टी>> इनवोकएल (संग्रह <? कॉल करने योग्य <टी >> कार्य बढ़ाता है) इंटरप्टेड एक्सेप्शन फेंकता है;

ExecutorService को कॉल करने योग्य कार्यों की एक सूची पास करता है । फ्यूचर्स की एक सूची देता है जिससे हम कार्य का परिणाम प्राप्त कर सकते हैं। जब सभी सबमिट किए गए कार्य पूरे हो जाते हैं तो यह सूची वापस आ जाती है।

यदि कार्य संग्रह संशोधित किया गया है, जबकि विधि चल रही है, तो इस विधि का परिणाम अपरिभाषित है।

<टी> सूची <भविष्य <टी>> इनवोक ऑल (संग्रह <? कॉल करने योग्य <टी>> कार्य, लंबे समय तक, टाइमयूनिट यूनिट) बढ़ाता है इंटरप्टेड एक्सेप्शन फेंकता है;

ExecutorService को कॉल करने योग्य कार्यों की एक सूची पास करता है । फ्यूचर्स की एक सूची देता है जिससे हम कार्य का परिणाम प्राप्त कर सकते हैं। यह सूची तब वापस आती है जब सभी पास किए गए कार्य पूरे हो जाते हैं, या समय समाप्त होने के बाद विधि समाप्त हो जाती है, जो भी पहले आता है।

यदि समय समाप्त हो जाता है, तो अधूरे कार्य रद्द कर दिए जाते हैं।

नोट: यह संभव है कि रद्द किया गया कार्य चलना बंद न हो (हम उदाहरण में इस दुष्प्रभाव को देखेंगे)।

यदि कार्य संग्रह संशोधित किया गया है, जबकि विधि चल रही है, तो इस विधि का परिणाम अपरिभाषित है।

<टी> टी इनवोक एनी (संग्रह <? कॉल करने योग्य <टी >> कार्य बढ़ाता है) इंटरप्टेड अपवाद, निष्पादन अपवाद फेंकता है;

ExecutorService को कॉल करने योग्य कार्यों की एक सूची पास करता है । किसी एक कार्य (यदि कोई हो) का परिणाम लौटाता है जो बिना किसी अपवाद (यदि कोई हो) को फेंके बिना पूरा हुआ।

यदि कार्य संग्रह संशोधित किया गया है, जबकि विधि चल रही है, तो इस विधि का परिणाम अपरिभाषित है।

<टी> टी इनवोकएनी (संग्रह <? कॉल करने योग्य <टी>> कार्य, लंबे समय तक, टाइमयूनिट यूनिट) का विस्तार करता है इंटरप्टेड अपवाद, निष्पादन अपवाद, टाइमआउट अपवाद;

ExecutorService को कॉल करने योग्य कार्यों की एक सूची पास करता है । किसी एक कार्य (यदि कोई हो) का परिणाम लौटाता है जो बिना किसी अपवाद के पूरा हो जाता है, इससे पहले कि विधि को समय समाप्त हो गया हो।

यदि कार्य संग्रह संशोधित किया गया है, जबकि विधि चल रही है, तो इस विधि का परिणाम अपरिभाषित है।

आइए निष्पादक सेवा के साथ काम करने का एक छोटा सा उदाहरण देखें ।


import java.util.List;
import java.util.concurrent.*;

public class ExecutorServiceTest {
   public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {
//Create an ExecutorService for 2 threads
       java.util.concurrent.ExecutorService executorService = new ThreadPoolExecutor(2, 2, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10));
// Create 5 tasks
       MyRunnable task1 = new MyRunnable();
       MyRunnable task2 = new MyRunnable();
       MyRunnable task3 = new MyRunnable();
       MyRunnable task4 = new MyRunnable();
       MyRunnable task5 = new MyRunnable();

       final List<MyRunnable> tasks = List.of(task1, task2, task3, task4, task5);
// Pass a list that contains the 5 tasks we created
       final List<Future<Void>> futures = executorService.invokeAll(tasks, 6, TimeUnit.SECONDS);
       System.out.println("Futures received");

// Stop the ExecutorService
       executorService.shutdown();

       try {
           TimeUnit.SECONDS.sleep(3);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }

       System.out.println(executorService.isShutdown());
       System.out.println(executorService.isTerminated());
   }

   public static class MyRunnable implements Callable<Void> {

       @Override
       public void call() {
// Add 2 delays. When the ExecutorService is stopped, we will see which delay is in progress when the attempt is made to stop execution of the task
           try {
               TimeUnit.SECONDS.sleep(3);
           } catch (InterruptedException e) {
               System.out.println("sleep 1: " + e.getMessage());
           }
           try {
               TimeUnit.SECONDS.sleep(2);
           } catch (InterruptedException e) {
               System.out.println("sleep 2: " + e.getMessage());
           }
           System.out.println("done");
           return null;
       }
   }
}

आउटपुट:

किया
गया
वायदा प्राप्त
नींद 1: नींद बाधित
नींद 1: नींद बाधित हो
गई सही सच


प्रत्येक कार्य 5 सेकंड के लिए चलता है। हमने दो धागों के लिए एक पूल बनाया है, इसलिए आउटपुट की पहली दो पंक्तियाँ सही समझ में आती हैं।

कार्यक्रम शुरू होने के छह सेकंड बाद, इनवॉकऑल विधि का समय समाप्त हो जाता है और परिणाम फ्यूचर्स की सूची के रूप में वापस आ जाता है । इसे प्राप्त आउटपुट स्ट्रिंग Futures से देखा जा सकता है ।

पहले दो काम पूरे होने के बाद दो और शुरू होते हैं। लेकिन क्योंकि इनवोकएल विधि में निर्धारित समय समाप्त हो गया है, इन दो कार्यों को पूरा करने का समय नहीं है। उन्हें "रद्द करें" आदेश प्राप्त होता है। इसलिए आउटपुट में स्लीप 1: स्लीप इंटरप्टेड के साथ दो लाइनें हैं ।

और फिर आप दो और पंक्तियों को did के साथ देख सकते हैं । यह वह साइड इफेक्ट है जिसका मैंने इनवोकअल विधि का वर्णन करते समय उल्लेख किया था।

पाँचवाँ और अंतिम कार्य कभी भी शुरू नहीं होता है, इसलिए हम इसके बारे में आउटपुट में कुछ भी नहीं देखते हैं।

आखिरी दो पंक्तियां शटडाउन और टर्मिनेटेड विधियों को कॉल करने का परिणाम हैं।

इस उदाहरण को डिबग मोड में चलाना भी दिलचस्प है और समय समाप्त होने के बाद कार्य की स्थिति को देखें ( निष्पादक सेवा.शटडाउन (); के साथ लाइन पर ब्रेकपॉइंट सेट करें ):

हम देखते हैं कि दो कार्य सामान्य रूप से पूर्ण हुए , और तीन कार्य "रद्द" हुए ।

अनुसूचित निष्पादक सेवा

निष्पादकों की हमारी चर्चा को समाप्त करने के लिए, आइए एक नज़र डालते हैं ScheduledExecutorService पर ।

इसकी 4 विधियाँ हैं:

तरीका विवरण
सार्वजनिक अनुसूचित भविष्य <?> अनुसूची (चलाने योग्य कमांड, लंबी देरी, TimeUnit इकाई); एक तर्क के रूप में निर्दिष्ट देरी के बाद एक बार चलने के लिए पास किए गए रननेबल कार्य को शेड्यूल करता है ।
सार्वजनिक <V> शेड्यूल्ड फ्यूचर <V> शेड्यूल (कॉल करने योग्य <V> कॉल करने योग्य, लंबी देरी, TimeUnit इकाई); एक तर्क के रूप में निर्दिष्ट देरी के बाद एक बार चलने के लिए पास किए गए कॉल करने योग्य कार्य को शेड्यूल करता है ।
सार्वजनिक अनुसूचित भविष्य <?> शेड्यूलएटफिक्स्ड रेट (रन करने योग्य कमांड, लंबी प्रारंभिक देरी, लंबी अवधि, टाइम यूनिट इकाई); समय-समय पर पास किए गए कार्य का निष्पादन शेड्यूल करता है, जिसे पहली बार इनिशियल डेले के बाद निष्पादित किया जाएगा , और प्रत्येक बाद का रन पीरियड के बाद शुरू होगा ।
सार्वजनिक अनुसूचित भविष्य <?> शेड्यूलविथफिक्स्डडेले (रन करने योग्य कमांड, लंबी प्रारंभिक देरी, लंबी देरी, टाइम यूनिट इकाई); समय-समय पर पास किए गए कार्य का निष्पादन शेड्यूल करता है, जिसे पहली बार इनिशियल डेले के बाद निष्पादित किया जाएगा , और प्रत्येक बाद का रन देरी के बाद शुरू होगा (पिछले रन के पूरा होने और वर्तमान के शुरू होने के बीच की अवधि)।