newCachedThreadPool()
बनाम newFixedThreadPool()
मुझे एक या दूसरे का उपयोग कब करना चाहिए? संसाधन उपयोग के संदर्भ में कौन सी रणनीति बेहतर है?
newCachedThreadPool()
बनाम newFixedThreadPool()
मुझे एक या दूसरे का उपयोग कब करना चाहिए? संसाधन उपयोग के संदर्भ में कौन सी रणनीति बेहतर है?
जवाबों:
मुझे लगता है कि डॉक्स इन दोनों कार्यों के अंतर और उपयोग को बहुत अच्छी तरह से समझाते हैं:
एक थ्रेड पूल बनाता है जो एक साझा अनबाउंड कतार से ऑपरेटिंग थ्रेड की निश्चित संख्या का पुन: उपयोग करता है। किसी भी बिंदु पर, अधिकांश nhhreads में सक्रिय प्रसंस्करण कार्य होंगे। यदि सभी थ्रेड्स सक्रिय होने पर अतिरिक्त कार्य सबमिट किए जाते हैं, तो वे कतार में प्रतीक्षा करेंगे जब तक एक थ्रेड उपलब्ध नहीं होगा। यदि बंद करने से पहले निष्पादन के दौरान विफलता के कारण कोई भी धागा समाप्त हो जाता है, तो बाद के कार्यों को निष्पादित करने के लिए आवश्यक होने पर एक नया स्थान लेगा। पूल में धागे तब तक मौजूद रहेंगे जब तक कि यह स्पष्ट रूप से बंद नहीं हो जाता।
एक थ्रेड पूल बनाता है जो आवश्यकतानुसार नए थ्रेड बनाता है, लेकिन जब वे उपलब्ध होते हैं तो पहले निर्मित थ्रेड का फिर से उपयोग करेंगे। ये पूल आम तौर पर कई अल्पकालिक अतुल्यकालिक कार्यों को निष्पादित करने वाले कार्यक्रमों के प्रदर्शन में सुधार करेंगे। निष्पादित करने के लिए कॉल उपलब्ध होने पर पहले से निर्मित थ्रेड का पुन: उपयोग करेगा। यदि कोई मौजूदा धागा उपलब्ध नहीं है, तो एक नया धागा बनाया जाएगा और पूल में जोड़ा जाएगा। साठ सेकंड के लिए उपयोग नहीं किए गए थ्रेड समाप्त हो जाते हैं और कैश से निकाल दिए जाते हैं। इस प्रकार, एक पूल जो लंबे समय तक निष्क्रिय रहता है, वह किसी भी संसाधन का उपभोग नहीं करेगा। ध्यान दें कि समान गुणों वाले लेकिन विभिन्न विवरणों (उदाहरण के लिए, टाइमआउट पैरामीटर) के साथ थ्रेडपूल एक्ज़ीक्यूटर कंस्ट्रक्टरों का उपयोग करके बनाया जा सकता है।
संसाधनों के संदर्भ में, newFixedThreadPool
सभी थ्रेड को तब तक चालू रखेंगे जब तक कि वे स्पष्ट रूप से समाप्त नहीं हो जाते। जिन newCachedThreadPool
थ्रेड्स में साठ सेकेंड का उपयोग नहीं किया गया है उन्हें समाप्त कर कैश से हटा दिया जाता है।
इसे देखते हुए, संसाधन की खपत स्थिति में बहुत अधिक निर्भर करेगी। उदाहरण के लिए, यदि आपके पास लंबे समय तक चलने वाले कार्यों की एक बड़ी संख्या है, तो मैं सुझाव दूंगा FixedThreadPool
। के रूप में CachedThreadPool
, डॉक्स का कहना है कि "ये पूल आमतौर पर उन कार्यक्रमों के प्रदर्शन में सुधार करेंगे जो कई अल्पकालिक अतुल्यकालिक कार्यों को निष्पादित करते हैं"।
newCachedThreadPool
कुछ गंभीर मुद्दों का कारण हो सकता है क्योंकि आप सभी नियंत्रण को छोड़ देते हैं thread pool
और जब सेवा एक ही होस्ट में दूसरों के साथ काम कर रही होती है , जो लंबे समय तक सीपीयू प्रतीक्षा के कारण दूसरों के दुर्घटनाग्रस्त हो सकती है। इसलिए मुझे लगता है कि newFixedThreadPool
इस तरह के परिदृश्य में अधिक सुरक्षित हो सकता है। साथ ही यह पोस्ट उनके बीच सबसे उत्कृष्ट मतभेदों को स्पष्ट करता है।
अन्य उत्तरों को पूरा करने के लिए, मैं जोशुआ बलोच, अध्याय 10, आइटम 68 द्वारा प्रभावी जावा, द्वितीय संस्करण को उद्धृत करना चाहूंगा:
"किसी विशेष एप्लिकेशन के लिए निष्पादक सेवा का चयन करना मुश्किल हो सकता है। यदि आप Executors.new- CachedThreadPool का उपयोग करके एक छोटा प्रोग्राम , या एक हल्के से लोड किए गए सर्वर को लिख रहे हैं , तो यह आमतौर पर एक अच्छा विकल्प है , क्योंकि यह कोई कॉन्फ़िगरेशन की मांग नहीं करता है और आम तौर पर" प्रकाश करता है सही बात।" लेकिन एक कैश्ड धागा पूल एक भारी लोड उत्पादन सर्वर के लिए एक अच्छा विकल्प नहीं है !
एक में कैश की गई थ्रेड पूल , प्रस्तुत कार्यों पंक्तिबद्ध नहीं कर रहे हैं लेकिन तुरंत निष्पादन के लिए एक धागा को हस्तांतरित कर दिया। यदि कोई थ्रेड उपलब्ध नहीं हैं, तो एक नया बनाया जाता है । यदि कोई सर्वर इतना अधिक भरा हुआ है कि उसके सभी सीपीयू पूरी तरह से उपयोग किए जाते हैं, और अधिक कार्य आते हैं, तो अधिक थ्रेड बनाए जाएंगे, जो केवल मामलों को बदतर बना देगा।
इसलिए, एक अत्यधिक भरी हुई उत्पादन सर्वर में , आप Executors.newFixedThreadPool का उपयोग करके बहुत बेहतर हैं , जो आपको अधिकतम नियंत्रण के लिए थ्रेड्स की एक निश्चित संख्या के साथ, या सीधे थ्रेडपूल एक्ज़ीक्यूटर क्लास का उपयोग करके देता है । "
यदि आप स्रोत कोड को देखते हैं, तो आप देखेंगे, वे थ्रेडपूल एक्ज़ीक्यूटर कह रहे हैं । आंतरिक रूप से और उनके गुणों को स्थापित करना। अपनी आवश्यकता का बेहतर नियंत्रण करने के लिए आप अपना निर्माण कर सकते हैं।
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
यदि आप Callable / Runnable कार्यों की एक अनबाउंड कतार के बारे में चिंतित नहीं हैं, तो आप उनमें से किसी एक का उपयोग कर सकते हैं। जैसा कि ब्रूनो ने सुझाव दिया है, मैं भी इन दोनों newFixedThreadPool
को पसंद करता हूं newCachedThreadPool
।
लेकिन ThreadPoolExecutornewFixedThreadPool
या तो की तुलना में अधिक लचीली सुविधाएँ प्रदान करता हैnewCachedThreadPool
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
RejectedExecutionHandler handler)
लाभ:
आपके पास BlockingQueue आकार का पूर्ण नियंत्रण है । यह अन-बाउंड नहीं है, पहले के दो विकल्पों के विपरीत। सिस्टम में अनपेक्षित अशांति होने पर कॉल करने योग्य / चलाने योग्य कार्यों के विशाल ढेर के कारण मुझे मेमोरी त्रुटि नहीं मिलेगी।
आप कस्टम अस्वीकृति हैंडलिंग नीति को लागू कर सकते हैं या नीतियों में से एक का उपयोग कर सकते हैं:
डिफॉल्ट में ThreadPoolExecutor.AbortPolicy
, हैंडलर अस्वीकृति पर एक रनटाइम रिजेक्टेड एक्सेप्शन एक्सप्रेशन फेंकता है।
में ThreadPoolExecutor.CallerRunsPolicy
, जिस थ्रेड को निष्पादित करता है वह कार्य चलाता है। यह एक सरल प्रतिक्रिया नियंत्रण तंत्र प्रदान करता है जो नए कार्यों को प्रस्तुत करने की दर को धीमा कर देगा।
में ThreadPoolExecutor.DiscardPolicy
, एक कार्य जिसे निष्पादित नहीं किया जा सकता है, बस गिरा दिया जाता है।
में ThreadPoolExecutor.DiscardOldestPolicy
, अगर निष्पादक बंद न किया गया, काम कतार के सिर पर कार्य गिरा दिया है, और फिर पुन: प्रयास निष्पादन किया जाता है (जो फिर से असफल हो सकता है, जिससे यह दोहराया जाना।)
आप निम्न उपयोग के मामलों के लिए एक कस्टम थ्रेड फैक्टरी को लागू कर सकते हैं:
यह सही है, Executors.newCachedThreadPool()
सर्वर कोड के लिए एक बढ़िया विकल्प नहीं है जो कई ग्राहकों और समवर्ती अनुरोधों की सेवा कर रहा है।
क्यों? इसके साथ मूल रूप से दो (संबंधित) समस्याएं हैं:
यह अनबाउंड है, जिसका अर्थ है कि आप किसी के लिए अपने JVM को अपंग करने के लिए केवल सेवा (DoS attack) में और अधिक काम इंजेक्ट करके दरवाजा खोल रहे हैं। थ्रेड्स स्मृति की गैर-नगण्य मात्रा का उपभोग करते हैं और उनके कार्य-प्रगति के आधार पर मेमोरी की खपत भी बढ़ाते हैं, इसलिए इस तरह से एक सर्वर को टोल करना काफी आसान है (जब तक कि आपके पास अन्य सर्किट-ब्रेकर न हों)।
निर्बाध समस्या को इस तथ्य से तेज किया जाता है कि एक्ज़ीक्यूसर को सामने रखा जाता है, SynchronousQueue
जिसका अर्थ है कि टास्क देने वाले और थ्रेड पूल के बीच सीधा सीधा संबंध है। प्रत्येक नया कार्य एक नया थ्रेड तैयार करेगा यदि सभी मौजूदा थ्रेड्स व्यस्त हैं। यह आमतौर पर सर्वर कोड के लिए एक खराब रणनीति है। जब सीपीयू संतृप्त हो जाता है, तो मौजूदा कार्य समाप्त होने में अधिक समय लेते हैं। फिर भी अधिक कार्य प्रस्तुत किए जा रहे हैं और अधिक थ्रेड बनाए गए हैं, इसलिए कार्य पूर्ण होने में अधिक समय और अधिक समय लेते हैं। जब सीपीयू संतृप्त होता है, तो अधिक थ्रेड्स निश्चित रूप से सर्वर की आवश्यकता नहीं होती है।
यहाँ मेरी सिफारिशें हैं:
एक निश्चित आकार के थ्रेड पूल का उपयोग करें Executors.newFixedThreadPool या एक ThreadPoolExecutor। धागे की एक अधिकतम सेट के साथ;
ThreadPoolExecutor
वर्ग निष्पादकों कि के कई से लौटाए जाते हैं के लिए आधार कार्यान्वयन है Executors
कारखाने तरीकों। तो आइए फिक्स्ड और कैश्ड थ्रेड पूल के ThreadPoolExecutor
दृष्टिकोण से देखें।
इस वर्ग का मुख्य निर्माता इस तरह दिखता है:
public ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler
)
corePoolSize
लक्ष्य थ्रेड पूल का न्यूनतम आकार निर्धारित करता है। क्रियान्वयन उस आकार का एक पूल बनाए रखेगा भले ही निष्पादन के लिए कोई कार्य न हो।
maximumPoolSize
कि एक ही बार में सक्रिय हो सकता है धागे की अधिकतम संख्या है।
थ्रेड पूल बढ़ने और corePoolSize
थ्रेशोल्ड से बड़ा हो जाने के बाद , निष्पादक निष्क्रिय थ्रेड को समाप्त कर सकता है और corePoolSize
फिर से पहुंच सकता है । यदि allowCoreThreadTimeOut
यह सच है, तो निष्पादक कोर पूल थ्रेड्स को भी समाप्त कर सकता है यदि वे keepAliveTime
थ्रेशोल्ड से अधिक निष्क्रिय थे ।
तो लब्बोलुआब यह है कि अगर धागे keepAliveTime
थ्रेशोल्ड से अधिक निष्क्रिय रहते हैं , तो वे समाप्त हो सकते हैं क्योंकि उनके लिए कोई मांग नहीं है।
क्या होता है जब कोई नया कार्य आता है और सभी कोर थ्रेड्स पर कब्जा कर लिया जाता है? नए कार्यों को उस BlockingQueue<Runnable>
उदाहरण के अंदर कतारबद्ध किया जाएगा । जब एक धागा मुक्त हो जाता है, तो उन पंक्तिबद्ध कार्यों में से एक को संसाधित किया जा सकता है।
BlockingQueue
जावा में इंटरफ़ेस के अलग-अलग कार्यान्वयन हैं , इसलिए हम विभिन्न कतारबद्ध तरीकों को लागू कर सकते हैं जैसे:
बाउंडेड क्यू : नए कार्यों को एक बाउंड टास्क कतार के अंदर कतारबद्ध किया जाएगा।
अनबाउंड क्यू : नए कार्यों को एक अनबाउंड कार्य कतार के अंदर कतारबद्ध किया जाएगा। तो यह कतार उतनी ही बढ़ सकती है जितना कि ढेर का आकार अनुमति देता है।
सिंक्रोनस हैंडऑफ : हम SynchronousQueue
नए कार्यों को कतारबद्ध करने के लिए भी उपयोग कर सकते हैं। उस स्थिति में, जब किसी नए कार्य को पंक्तिबद्ध किया जाता है, तो उस कार्य के लिए पहले से ही एक और सूत्र की प्रतीक्षा करनी चाहिए।
यहां बताया गया है कि ThreadPoolExecutor
नया कार्य कैसे निष्पादित किया जाता है:
corePoolSize
थ्रेड से कम चल रहे हैं, तो दिए गए कार्य के साथ एक नया धागा शुरू करने की कोशिश करता है।BlockingQueue#offer
विधि का उपयोग करके नए कार्य को लागू करने की कोशिश करता है
। offer
यदि कतार भर गई है और तुरंत वापस लौट विधि ब्लॉक नहीं false
।offer
रिटर्न false
) को पंक्तिबद्ध करने में विफल रहता है , तो यह इस कार्य के साथ थ्रेड पूल में एक नया धागा जोड़ने की कोशिश करता है।RejectedExecutionHandler
।फिक्स्ड और कैश्ड थ्रेड पूल के बीच मुख्य अंतर इन तीन कारकों को उबालता है:
+ ----------- + ----------- + ------------------- + ----- ---------------------------- + | पूल प्रकार | कोर का आकार | अधिकतम आकार | कतार की रणनीति | + ----------- + ----------- + ------------------- + ----- ---------------------------- + | फिक्स्ड | n (निश्चित) | n (निश्चित) | अनबाउंड `लिंक्डब्लॉकिंग क्यू` + ----------- + ----------- + ------------------- + ----- ---------------------------- + | कैश्ड | 0 | Integer.MAX_VALUE | `तुल्यकालिक क्यू '| + ----------- + ----------- + ------------------- + ----- ---------------------------- +
Excutors.newFixedThreadPool(n)
काम करता है:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
जैसा कि आप देख सकते हैं:
OutOfMemoryError
।मुझे एक या दूसरे का उपयोग कब करना चाहिए? संसाधन उपयोग के संदर्भ में कौन सी रणनीति बेहतर है?
जब हम संसाधन प्रबंधन उद्देश्यों के लिए समवर्ती कार्यों की संख्या को सीमित करने जा रहे हैं, तो एक निश्चित आकार का थ्रेड पूल एक अच्छा उम्मीदवार प्रतीत होता है ।
उदाहरण के लिए, यदि हम वेब सर्वर अनुरोधों को संभालने के लिए एक निष्पादक का उपयोग करने जा रहे हैं, तो एक निश्चित निष्पादक अनुरोध को अधिक उचित रूप से फट सकता है।
और भी बेहतर संसाधन प्रबंधन के लिए, यह उचित के साथ ThreadPoolExecutor
एक बंधे हुए BlockingQueue<T>
कार्यान्वयन के साथ एक कस्टम बनाने के लिए अत्यधिक अनुशंसित है RejectedExecutionHandler
।
यहाँ कैसे Executors.newCachedThreadPool()
काम करता है:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
जैसा कि आप देख सकते हैं:
Integer.MAX_VALUE
। व्यावहारिक रूप से, थ्रेड पूल अनबाउंड है।SynchronousQueue
हमेशा विफल हो जाती है जब इसे स्वीकार करने के लिए दूसरे छोर पर कोई नहीं होता है!मुझे एक या दूसरे का उपयोग कब करना चाहिए? संसाधन उपयोग के संदर्भ में कौन सी रणनीति बेहतर है?
जब आपके पास बहुत-से पूर्वानुमानित छोटे-छोटे कार्य हों, तो इसका उपयोग करें।
आपको केवल नए कैश्ड थ्रेडपूल का उपयोग करना होगा जब आपके पास जावदोक में बताए गए कम-से-कम अतुल्यकालिक कार्य हों, यदि आप ऐसे कार्यों को प्रस्तुत करते हैं जो प्रक्रिया में अधिक समय लेते हैं, तो आप बहुत सारे थ्रेड्स बनाना समाप्त कर देंगे। यदि आप newCachedThreadPool ( http://rashcoder.com/be-careful-ORE-using-executors-newcachedthreadpool/ ) पर तेज़ गति से लंबे समय तक चलने वाले कार्य सबमिट करते हैं, तो आप 100% CPU को हिट कर सकते हैं ।
मैं कुछ त्वरित परीक्षण करता हूं और निम्नलिखित निष्कर्ष हैं:
1) अगर सिंक्रोनस क्यू का उपयोग कर:
थ्रेड्स अधिकतम आकार तक पहुंचने के बाद, किसी भी नए काम को नीचे दिए गए अपवाद के साथ अस्वीकार कर दिया जाएगा।
धागे में अपवाद "मुख्य" java.util.concurrent.RejectedExecutionException: टास्क java.util.concurrent.FutureTask@3fee733d java.util.concurrent.ThreadPoolExecutor@5acf9800 से खारिज कर दिया [चल रहा है, पूल आकार = 3, सक्रिय थ्रेड = 3। = 0, पूर्ण कार्य = 0]
java.util.concurrent.ThreadPoolExecutor $ AbortPolicy.rejectedExecution (ThreadPoolExecutor.java:2047) पर
2) अगर लिंक्डब्लॉकिंग क्यू का उपयोग कर:
धागे कभी भी न्यूनतम आकार से अधिकतम आकार तक नहीं बढ़ते हैं, जिसका अर्थ है कि थ्रेड पूल न्यूनतम आकार के रूप में तय किया गया है।