तुम्हाला एक्झिक्युटर इंटरफेसची गरज का आहे?

Java 5 च्या आधी, तुम्हाला तुमच्या ऍप्लिकेशनमध्ये तुमचे स्वतःचे सर्व कोड थ्रेड व्यवस्थापन लिहावे लागले. याव्यतिरिक्त, तयार करणे एनवीन धागाऑब्जेक्ट एक संसाधन-केंद्रित ऑपरेशन आहे, आणि प्रत्येक हलक्या कार्यासाठी नवीन धागा तयार करण्यात अर्थ नाही. आणि ही समस्या मल्टी-थ्रेडेड ऍप्लिकेशन्सच्या प्रत्येक विकसकाला परिचित असल्याने, त्यांनी ही कार्यक्षमता जावामध्ये एक्झिक्युटर फ्रेमवर्क म्हणून आणण्याचा निर्णय घेतला.

मोठी कल्पना काय आहे? हे सोपे आहे: प्रत्येक नवीन कार्यासाठी नवीन धागा तयार करण्याऐवजी, थ्रेड एका प्रकारच्या "स्टोरेज" मध्ये ठेवले जातात आणि जेव्हा नवीन कार्य येते, तेव्हा आम्ही नवीन तयार करण्याऐवजी विद्यमान थ्रेड पुनर्प्राप्त करतो.

या फ्रेमवर्कचे मुख्य इंटरफेस आहेत Executor , ExecutorService आणि ScheduledExecutorService , त्यापैकी प्रत्येक मागील एकाची कार्यक्षमता वाढवते.

एक्झिक्युटर इंटरफेस हा बेस इंटरफेस आहे. हे एकल व्हॉइड एक्झिक्यूट (रन करण्यायोग्य कमांड) पद्धत घोषित करते जी रन करण्यायोग्य ऑब्जेक्टद्वारे लागू केली जाते.

ExecutorService इंटरफेस अधिक मनोरंजक आहे . त्यात काम पूर्ण करण्याच्या पद्धती तसेच काही प्रकारचे परिणाम परत करण्याच्या पद्धती आहेत. चला त्याच्या पद्धतींचा बारकाईने विचार करूया:

पद्धत वर्णन
शून्य शटडाउन(); या पद्धतीला कॉल केल्याने ExecutorService थांबते . प्रक्रियेसाठी आधीच सबमिट केलेली सर्व कार्ये पूर्ण केली जातील, परंतु नवीन कार्ये स्वीकारली जाणार नाहीत.
सूची<Runnable> shutdownNow();

या पद्धतीला कॉल केल्याने ExecutorService थांबते . प्रक्रियेसाठी आधीच सबमिट केलेल्या सर्व कार्यांसाठी Thread.interrupt कॉल केला जाईल. ही पद्धत रांगेत असलेल्या कार्यांची यादी देते.

पद्धत कॉल केलेल्या वेळी "प्रगतीमध्ये" असलेली सर्व कार्ये पूर्ण होण्याची प्रतीक्षा करत नाही.

चेतावणी: या पद्धतीला कॉल केल्याने संसाधने लीक होऊ शकतात.

बुलियन isShutdown(); ExecutorService थांबली आहे का ते तपासते .
बुलियन isTerminated(); ExecutorService बंद केल्यानंतर सर्व कार्ये पूर्ण झाली असल्यास खरे मिळवते . जोपर्यंत shutdown() किंवा shutdownNow() कॉल केले जात नाही तोपर्यंत ते नेहमी खोटे परत येईल .
बूलियन awaitTermination(लांब कालबाह्य, TimeUnit युनिट) InterruptedException थ्रो करते;

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

  • सर्व नियोजित कार्ये पूर्ण झाली आहेत;
  • पद्धतीला दिलेली कालबाह्यता निघून गेली आहे;
  • वर्तमान धागा व्यत्यय आला आहे.

सर्व कार्ये पूर्ण झाल्यास सत्य मिळवते आणि समाप्तीपूर्वी कालबाह्य झाल्यास असत्य मिळवते .

<T> भविष्य<T> सबमिट (कॉल करण्यायोग्य<T> कार्य);

ExecutorService मध्ये कॉल करण्यायोग्य कार्य जोडते आणि भविष्यातील इंटरफेस लागू करणारी ऑब्जेक्ट परत करते .

<T> हा उत्तीर्ण कार्याच्या निकालाचा प्रकार आहे.

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

ExecutorService मध्ये रन करण्यायोग्य कार्य जोडते आणि भविष्यातील इंटरफेस लागू करणारी ऑब्जेक्ट परत करते .

टी रिझल्ट पॅरामीटर म्हणजे प्राप्त झालेल्या get() मेथडला कॉल करून परतावा मिळतोभविष्यातील वस्तू.

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

ExecutorService मध्ये रन करण्यायोग्य कार्य जोडते आणि भविष्यातील इंटरफेस लागू करणारी ऑब्जेक्ट परत करते .

परिणामी Future ऑब्जेक्टवर get() मेथड कॉल केल्यास null मिळेल.

<T> List<Future<T>> invokeAll(Collection<? expends Callable<T>> tasks) Throughs InterruptedException;

ExecutorService ला कॉल करण्यायोग्य कार्यांची यादी पास करते . फ्युचर्सची यादी देते ज्यावरून आपण कामाचे परिणाम मिळवू शकतो. सर्व सबमिट केलेली कार्ये पूर्ण झाल्यावर ही यादी परत केली जाते.

पद्धत चालू असताना कार्ये संकलन सुधारित केले असल्यास , या पद्धतीचा परिणाम अपरिभाषित आहे.

<T> List<Future<T>> invokeAll(Collection<? expends Callable<T>> कार्ये, दीर्घ कालबाह्य, TimeUnit युनिट) InterruptedException थ्रो करते;

ExecutorService ला कॉल करण्यायोग्य कार्यांची यादी पास करते . फ्युचर्सची यादी देते ज्यावरून आपण कामाचे परिणाम मिळवू शकतो. सर्व उत्तीर्ण केलेली कार्ये पूर्ण झाल्यावर, किंवा पद्धतीला दिलेली कालबाह्यता संपल्यानंतर, यापैकी जे प्रथम येईल तेंव्हा ही यादी परत केली जाते.

कालबाह्य झाल्यास, अपूर्ण कार्ये रद्द केली जातात.

टीप: हे शक्य आहे की रद्द केलेले कार्य चालू होणार नाही (आम्ही हा दुष्परिणाम उदाहरणामध्ये पाहू).

पद्धत चालू असताना कार्ये संकलन सुधारित केले असल्यास , या पद्धतीचा परिणाम अपरिभाषित आहे.

<T> T invokeAny(Collection<? expands Callable<T>> tasks) Throughs InterruptedException, ExecutionException;

ExecutorService ला कॉल करण्यायोग्य कार्यांची यादी पास करते . अपवाद न टाकता पूर्ण केलेल्या कार्यांपैकी (असल्यास) परिणाम मिळवते (असल्यास).

पद्धत चालू असताना कार्ये संकलन सुधारित केले असल्यास , या पद्धतीचा परिणाम अपरिभाषित आहे.

<T> T invokeAny(संग्रह<? कॉल करण्यायोग्य<T>> कार्ये, लाँग टाइमआउट, टाइमयुनिट युनिट) फेकते InterruptedException, ExecutionException, TimeoutException;

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 सेकंद चालते. आम्ही दोन थ्रेडसाठी एक पूल तयार केला आहे, त्यामुळे आउटपुटच्या पहिल्या दोन ओळी अचूक अर्थ देतात.

प्रोग्रॅम सुरू झाल्यानंतर सहा सेकंदांनंतर, सर्व पद्धतीची वेळ संपते आणि निकाल फ्यूचर्सची सूची म्हणून परत केला जातो . हे प्राप्त झालेल्या आउटपुट स्ट्रिंगमधून पाहिले जाऊ शकते .

पहिली दोन कामे पूर्ण झाल्यानंतर आणखी दोन सुरू होतात. परंतु invokeAll पद्धतीमध्ये सेट केलेली कालबाह्यता संपल्यामुळे, या दोन कार्यांना पूर्ण होण्यासाठी वेळ नाही. त्यांना "रद्द" आदेश प्राप्त होतो. म्हणूनच आउटपुटमध्ये स्लीप 1 सह दोन ओळी आहेत: स्लीप इंटरप्टेड .

आणि नंतर आपण पूर्ण सह आणखी दोन ओळी पाहू शकता . invokeAll पद्धतीचे वर्णन करताना मी नमूद केलेला हा दुष्परिणाम आहे .

पाचवे आणि अंतिम कार्य कधीच सुरू होत नाही, म्हणून आम्हाला आउटपुटमध्ये याबद्दल काहीही दिसत नाही.

शेवटच्या दोन ओळी isShutdown आणि isTerminated पद्धती कॉल केल्याचा परिणाम आहे .

हे उदाहरण डीबग मोडमध्ये चालवणे आणि कालबाह्य झाल्यानंतर कार्य स्थिती पाहणे देखील मनोरंजक आहे ( executorService.shutdown() सह लाइनवर ब्रेकपॉइंट सेट करा ):

आपण पाहतो की दोन कार्ये सामान्यपणे पूर्ण झाली आहेत आणि तीन कार्ये "रद्द" झाली आहेत .

शेड्यूल्ड एक्झिक्यूटरसेवा

एक्झिक्युटर्सबद्दलची आमची चर्चा पूर्ण करण्यासाठी, शेड्युल्डएक्सेक्युटरसर्व्हिसवर एक नजर टाकूया .

त्याच्या 4 पद्धती आहेत:

पद्धत वर्णन
सार्वजनिक शेड्यूल्ड फ्यूचर<?> शेड्यूल (चालता येण्याजोगा आदेश, दीर्घ विलंब, टाइमयुनिट युनिट); वितर्क म्हणून निर्दिष्ट केलेल्या विलंबानंतर एकदा चालण्यासाठी पास केलेले रन करण्यायोग्य कार्य शेड्यूल करते.
सार्वजनिक <V> शेड्यूल्ड फ्यूचर<V> वेळापत्रक(कॉल करण्यायोग्य<V> कॉल करण्यायोग्य, दीर्घ विलंब, टाइमयुनिट युनिट); वितर्क म्हणून निर्दिष्ट केलेल्या विलंबानंतर एकदा चालण्यासाठी पास केलेले कॉल करण्यायोग्य कार्य शेड्यूल करते.
सार्वजनिक शेड्यूल्ड फ्यूचर<?> शेड्यूलएटफिक्स्डरेट(चालता येण्याजोगा आदेश, दीर्घ आरंभिक विलंब, दीर्घ कालावधी, टाइमयुनिट युनिट); पास केलेल्या टास्कची नियतकालिक अंमलबजावणी शेड्यूल करते, जी प्रारंभिक विलंबानंतर प्रथमच कार्यान्वित केली जाईल आणि त्यानंतरची प्रत्येक धाव कालावधीनंतर सुरू होईल .
सार्वजनिक शेड्यूल्ड फ्यूचर<?> शेड्यूलविथफिक्स्डडेले(चालता येण्याजोगा कमांड, लाँग इनिशिअल डिले, लांब विलंब, टाइमयुनिट युनिट); पास केलेल्या टास्कची नियतकालिक अंमलबजावणी शेड्युल करते, जी इनिशियल डिले नंतर प्रथमच अंमलात आणली जाईल आणि त्यानंतरची प्रत्येक रन विलंबानंतर सुरू होईल (मागील रन पूर्ण होणे आणि सध्याच्या सुरू होण्याच्या दरम्यानचा कालावधी).