तुम्हाला 1 थ्रेडसाठी ExecutorService का आवश्यक आहे?

तुम्ही Executors.newSingleThreadExecutor पद्धत वापरू शकता ज्यामध्ये एकच थ्रेड समाविष्ट असलेल्या पूलसह ExecutorService तयार करा . पूलचा तर्क खालीलप्रमाणे आहे:

  • सेवा एका वेळी एकच कार्य करते.
  • आम्ही अंमलबजावणीसाठी N टास्क सबमिट केल्यास, सर्व N टास्क एकामागून एक एकाच थ्रेडद्वारे कार्यान्वित केल्या जातील.
  • थ्रेडमध्ये व्यत्यय आल्यास, उर्वरित कार्ये पूर्ण करण्यासाठी नवीन थ्रेड तयार केला जाईल.

चला अशा परिस्थितीची कल्पना करूया जिथे आमच्या प्रोग्रामला खालील कार्यक्षमतेची आवश्यकता आहे:

आम्‍हाला वापरकर्त्याच्‍या विनंत्यांची 30 सेकंदात प्रक्रिया करण्‍याची आवश्‍यकता आहे, परंतु प्रति युनिट वेळेत एकापेक्षा जास्त विनंती नाही.

आम्ही वापरकर्त्याच्या विनंतीवर प्रक्रिया करण्यासाठी कार्य वर्ग तयार करतो:


class Task implements Runnable {
   private final int taskNumber;

   public Task(int taskNumber) {
       this.taskNumber = taskNumber;
   }

   @Override
   public void run() {
       try {
           Thread.sleep(1000);
       } catch (InterruptedException ignored) {
       }
       System.out.printf("Processed request #%d on thread id=%d\\n", taskNumber, Thread.currentThread().getId());
   }
}
    

वर्ग येणार्‍या विनंतीवर प्रक्रिया करण्याच्या वर्तनाचे मॉडेल करतो आणि त्याचा क्रमांक प्रदर्शित करतो.

पुढे, मुख्य पद्धतीमध्ये, आम्ही 1 थ्रेडसाठी एक ExecutorService तयार करतो , ज्याचा वापर आम्ही येणार्‍या विनंत्यांवर क्रमाने प्रक्रिया करण्यासाठी करू. कार्य अटी "३० सेकंदांच्या आत" ठरवत असल्याने, आम्ही ३०-सेकंद प्रतीक्षा जोडतो आणि नंतर जबरदस्तीने ExecutorService थांबवतो .


public static void main(String[] args) throws InterruptedException {
   ExecutorService executorService = Executors.newSingleThreadExecutor();

   for (int i = 0; i < 1_000; i++) {
       executorService.execute(new Task(i));
   }
   executorService.awaitTermination(30, TimeUnit.SECONDS);
   executorService.shutdownNow();
}
    

प्रोग्राम सुरू केल्यानंतर, कन्सोल विनंती प्रक्रियेबद्दल संदेश प्रदर्शित करते:

थ्रेड आयडीवर प्रक्रिया केलेली विनंती #0 = 16
थ्रेड आयडीवर प्रक्रिया केलेली विनंती # 1 = 16 थ्रेड आयडीवर
प्रक्रिया केलेली विनंती # 2 = 16

थ्रेड आयडी = 16 वर प्रक्रिया केलेली विनंती # 29

३० सेकंदांच्या विनंतीवर प्रक्रिया केल्यानंतर, executorService shutdownNow() पद्धतीला कॉल करते , जे सध्याचे कार्य थांबवते (एक्झिक्युट केले जात आहे) आणि सर्व प्रलंबित कार्ये रद्द करते. त्यानंतर, कार्यक्रम यशस्वीरित्या संपतो.

परंतु सर्व काही नेहमीच इतके परिपूर्ण नसते, कारण आमच्या प्रोग्राममध्ये सहजपणे अशी परिस्थिती असू शकते जिथे आमच्या पूलच्या एकमेव थ्रेडद्वारे उचललेले एक कार्य चुकीचे कार्य करते आणि आमचा थ्रेड देखील संपुष्टात आणते. या प्रकरणात executorService एकाच थ्रेडसह कसे कार्य करते हे शोधण्यासाठी आम्ही या परिस्थितीचे अनुकरण करू शकतो .

हे करण्यासाठी, एक कार्य कार्यान्वित होत असताना, आम्ही असुरक्षित आणि अप्रचलित Thread.currentThread().stop() पद्धत वापरून आमचा थ्रेड बंद करतो. आम्ही हे जाणूनबुजून परिस्थितीचे अनुकरण करण्यासाठी करत आहोत जेथे कार्यांपैकी एक थ्रेड संपुष्टात आणतो.

आम्ही टास्क क्लासमध्ये रन पद्धत बदलू :


@Override
public void run() {
   try {
       Thread.sleep(1000);
   } catch (InterruptedException ignored) {
   }

   if (taskNumber == 5) {
       Thread.currentThread().stop();
   }

   System.out.printf("Processed request #%d on thread id=%d\\n", taskNumber, Thread.currentThread().getId());
}
    

आम्ही कार्य #5 मध्ये व्यत्यय आणू.

कार्य # 5 च्या शेवटी व्यत्यय आणलेल्या थ्रेडसह आउटपुट कसे दिसते ते पाहूया:

थ्रेड आयडीवर प्रक्रिया केलेली विनंती #0=16
थ्रेड आयडीवर प्रक्रिया केलेली विनंती #1=16
थ्रेड आयडीवर प्रक्रिया केलेली विनंती #2=16
थ्रेड आयडीवर प्रक्रिया केलेली विनंती #3=16 थ्रेड आयडीवर
प्रक्रिया केलेली विनंती #4=16
प्रक्रिया केलेली विनंती #6 रोजी थ्रेड आयडी=१७
थ्रेड आयडी=१७ वर प्रक्रिया केलेली विनंती #७

थ्रेड आयडी=१७ वर प्रक्रिया केलेली विनंती #२९

आम्ही पाहतो की टास्क 5 च्या शेवटी थ्रेडमध्ये व्यत्यय आल्यावर, टास्क थ्रेडमध्ये कार्यान्वित होऊ लागतात ज्याचा आयडेंटिफायर 17 आहे, जरी ते पूर्वी थ्रेडवर 16 आयडेंटिफायरसह कार्यान्वित केले गेले होते. आणि कारण आमच्या पूलमध्ये सिंगल थ्रेड, याचा अर्थ फक्त एकच असू शकतो: executorService ने थांबलेला थ्रेड नवीन थ्रेडने बदलला आणि कार्ये कार्यान्वित करणे सुरू ठेवले.

अशाप्रकारे, जेव्हा आम्हांला एकावेळी एकाच वेळी आणि फक्त एकाच टास्कवर प्रक्रिया करायची असेल तेव्हा आम्ही सिंगल-थ्रेड पूलसह newSingleThreadExecutor चा वापर केला पाहिजे आणि आम्हाला मागील टास्क पूर्ण झाल्याची पर्वा न करता रांगेतून कामांवर प्रक्रिया सुरू ठेवायची आहे (उदा. आमच्या कार्यांमुळे धागा नष्ट होतो).

थ्रेडफॅक्टरी

थ्रेड्स तयार करणे आणि पुन्हा तयार करणे याबद्दल बोलत असताना, आम्ही उल्लेख केल्याशिवाय मदत करू शकत नाहीथ्रेडफॅक्टरी.

थ्रेडफॅक्टरीमागणीनुसार नवीन धागे तयार करणारी वस्तू आहे.

आपण आपली स्वतःची थ्रेड निर्मिती फॅक्टरी तयार करू शकतो आणि त्याचे उदाहरण Executors.newSingleThreadExecutor(ThreadFactory threadFactory) पद्धतीला देऊ शकतो.


ExecutorService executorService = Executors.newSingleThreadExecutor(new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "MyThread");
            }
        });
                    
आम्ही नवीन थ्रेड तयार करण्याची पद्धत ओव्हरराइड करतो, थ्रेडचे नाव कन्स्ट्रक्टरला देतो.

ExecutorService executorService = Executors.newSingleThreadExecutor(new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                Thread thread = new Thread(r, "MyThread");
                thread.setPriority(Thread.MAX_PRIORITY);
                return thread;
            }
        });
                    
आम्ही तयार केलेल्या थ्रेडचे नाव आणि प्राधान्य बदलले आहे.

तर आपण पाहतो की आपल्याकडे 2 ओव्हरलोड केलेल्या Executors.newSingleThreadExecutor पद्धती आहेत. एक पॅरामीटर्सशिवाय आणि दुसरा थ्रेडफॅक्टरी पॅरामीटरसह.

ThreadFactory वापरून , तुम्ही तयार केलेले थ्रेड्स आवश्यकतेनुसार कॉन्फिगर करू शकता, उदाहरणार्थ, प्राधान्यक्रम सेट करून, थ्रेड उपवर्ग वापरून, थ्रेडमध्ये UncaughtExceptionHandler जोडून , ​​इत्यादी.