आपको 1 थ्रेड के लिए ExecutorService की आवश्यकता क्यों हो सकती है?

आप एक पूल के साथ ExecutorService बनाने के लिए Executors.newSingleThreadExecutor विधि का उपयोग कर सकते हैं जिसमें एक थ्रेड शामिल है। पूल का तर्क इस प्रकार है:

  • सेवा एक समय में केवल एक कार्य निष्पादित करती है।
  • यदि हम 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 थ्रेड के लिए एक एक्ज़ीक्यूटर सर्विस बनाते हैं, जिसका उपयोग हम आने वाले अनुरोधों को क्रमिक रूप से संसाधित करने के लिए करेंगे। चूंकि कार्य की शर्तें "30 सेकंड के भीतर" निर्धारित करती हैं, हम 30-सेकंड की प्रतीक्षा जोड़ते हैं और फिर 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

थ्रेड आईडी पर संसाधित अनुरोध #2=16

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

लेकिन सब कुछ हमेशा इतना सही नहीं होता है, क्योंकि हमारे कार्यक्रम में आसानी से ऐसी स्थिति हो सकती है जहां हमारे पूल के एकमात्र थ्रेड द्वारा उठाए गए कार्यों में से एक गलत तरीके से काम करता है और यहां तक ​​कि हमारे थ्रेड को समाप्त भी कर देता है। हम इस स्थिति को यह पता लगाने के लिए अनुकरण कर सकते हैं कि इस मामले में निष्पादक सेवा एक धागे के साथ कैसे काम करती है।

ऐसा करने के लिए, जबकि एक कार्य निष्पादित किया जा रहा है, हम असुरक्षित और अप्रचलित थ्रेड.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 पर थ्रेड आईडी = 17
संसाधित अनुरोध #7 थ्रेड आईडी पर = 17

संसाधित अनुरोध # 29 थ्रेड आईडी पर = 17

हम देखते हैं कि कार्य 5 के अंत में थ्रेड के बाधित होने के बाद, कार्य एक थ्रेड में निष्पादित होने लगते हैं जिसका पहचानकर्ता 17 है, हालाँकि उन्हें पहले थ्रेड पर निष्पादित किया गया था जिसका पहचानकर्ता 16 है। और क्योंकि हमारे पूल में एक है एकल धागा, इसका केवल एक ही मतलब हो सकता है: निष्पादक सेवा ने रुके हुए धागे को एक नए से बदल दिया और कार्यों को निष्पादित करना जारी रखा।

इस प्रकार, हमें सिंगल-थ्रेड पूल के साथ 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 तरीके हैं। एक पैरामीटर के बिना, और दूसरा थ्रेडफैक्ट्री पैरामीटर के साथ।

थ्रेडफ़ैक्टरी का उपयोग करके , आप बनाए गए थ्रेड्स को आवश्यकतानुसार कॉन्फ़िगर कर सकते हैं, उदाहरण के लिए, प्राथमिकताएँ सेट करके, थ्रेड उपवर्गों का उपयोग करके, थ्रेड में एक UncaughtExceptionHandler जोड़कर, और इसी तरह।