Thread Pool — एक थ्रेड प्रबंधन तंत्र है जिसमें पहले से बनाया गया थ्रेड्स का पूल कार्यों को निष्पादित करने के लिए पुनः उपयोग किया जाता है, जिससे थ्रेड बनाने और नष्ट करने के ओवरहेड से बचा जा सकता है। मोबाइल डेवलपमेंट में, थ्रेड पूल का उपयोग बैकग्राउंड ऑपरेशनों के लिए किया जाता है: नेटवर्क अनुरोध, इमेज प्रोसेसिंग, डेटाबेस के साथ काम करना। Google Android दस्तावेज़ीकरण (2025) के अनुसार, ExecutorService Android में बैकग्राउंड थ्रेड प्रबंधित करने का अनुशंसित तरीका है। iOS में, OperationQueue और GCD DispatchQueue वैश्विक concurrent कतारों के साथ समान भूमिका निभाते हैं।
मुख्य बिंदु
Thread Pool (थ्रेड पूल) — एक आर्किटेक्चरल पैटर्न है जिसमें थ्रेड्स की एक निश्चित संख्या पहले से बनाई जाती है और कई कार्यों को निष्पादित करने के लिए पुनः उपयोग की जाती है। प्रत्येक ऑपरेशन के लिए नया थ्रेड बनाने के बजाय (जो महँगा है: JVM में प्रति थ्रेड लगभग 1 MB स्टैक), कार्यों को एक कतार में रखा जाता है और पूल के खाली थ्रेड द्वारा निष्पादित किया जाता है। मोबाइल डेवलपमेंट में, थ्रेड पूल प्रदर्शन के लिए महत्वपूर्ण है — Android और iOS प्रति एप्लिकेशन थ्रेड्स की संख्या को सीमित करते हैं।
थ्रेड बनाना एक महँगा ऑपरेशन है: स्टैक आवंटन, सिस्टम पंजीकरण, संदर्भ स्विचिंग। सीमित संसाधनों वाले मोबाइल उपकरणों पर, थ्रेड का अनियंत्रित निर्माण Android पर OOM (OutOfMemoryError) और iOS पर थ्रॉटलिंग की ओर ले जाता है। Thread Pool दोनों समस्याओं को हल करता है: यह एक साथ चलने वाले थ्रेड्स की अधिकतम संख्या को सीमित करता है और पहले से बनाए गए थ्रेड्स का पुनः उपयोग करता है। Google raw Thread() के बजाय ExecutorService की सिफारिश करता है, Apple Thread के बजाय OperationQueue की सिफारिश करता है।
| पैरामीटर | बिना पूल के (raw Thread) | Thread Pool के साथ |
|---|---|---|
| थ्रेड निर्माण | प्रत्येक कार्य के लिए | पूल बनाते समय एक बार |
| अधिकतम थ्रेड | असीमित (OOM जोखिम) | core/max pool size द्वारा सीमित |
| उपयोग | कम (कार्य के बाद थ्रेड समाप्त) | उच्च (थ्रेड पुनः उपयोग होता है) |
| प्रबंधन | मैन्युअल (join, interrupt) | स्वचालित (ExecutorService) |
| मेमोरी खपत | प्रत्येक कार्य के साथ बढ़ती है | निश्चित |
थ्रेड पूल उत्पादक-उपभोक्ता सिद्धांत पर काम करता है: कार्य (Runnable/Callable) एक ब्लॉकिंग कतार (BlockingQueue) में रखे जाते हैं। पूल के थ्रेड कतार में कार्यों की प्रतीक्षा करते हैं और उन्हें निष्पादन के लिए लेते हैं। एल्गोरिदम: यदि खाली थ्रेड्स की संख्या corePoolSize से कम है, तो एक नया थ्रेड बनाया जाता है। यदि corePoolSize तक पहुँच गया है, तो कार्य कतार में रखा जाता है। यदि कतार भरी हुई है और थ्रेड्स की संख्या maximumPoolSize से कम है, तो एक अतिरिक्त थ्रेड बनाया जाता है। यदि maximumPoolSize पार हो गया है, तो कार्य RejectedExecutionHandler के माध्यम से अस्वीकार कर दिया जाता है।
Core pool size — थ्रेड्स की संख्या जो निष्क्रिय होने पर भी पूल में रखी जाती है। Maximum pool size — थ्रेड्स की अधिकतम संख्या जो कतार के ओवरफ़्लो होने पर बनाई जा सकती है। उनके बीच का अंतर अतिरिक्त (overflow) थ्रेड हैं जो अस्थायी रूप से बनाए जाते हैं और निष्क्रियता टाइमआउट के बाद समाप्त हो जाते हैं। मोबाइल उपकरणों पर, थ्रेड निर्माण से पीक लोड से बचने के लिए corePoolSize को maximumPoolSize के बराबर सेट करने की अनुशंसा की जाती है।
BlockingQueue निष्पादन की प्रतीक्षा कर रहे कार्यों को संग्रहीत करती है। सबसे लोकप्रिय कार्यान्वयन: LinkedBlockingQueue (असीमित), ArrayBlockingQueue (सीमित) और SynchronousQueue (बिना भंडारण — कार्य सीधे थ्रेड को भेजा जाता है)। जब कतार और पूल भर जाते हैं, RejectedExecutionHandler सक्रिय होता है। मानक नीतियाँ: AbortPolicy (RejectedExecutionException फेंकता है), CallerRunsPolicy (भेजने वाले के थ्रेड में निष्पादित करता है), DiscardPolicy और DiscardOldestPolicy।
// Android में Thread Pool बनाना
val threadPool = ThreadPoolExecutor(
corePoolSize = 2, // न्यूनतम 2 थ्रेड
maximumPoolSize = 4, // अधिकतम 4 थ्रेड
keepAliveTime = 30L, // ओवरफ़्लो थ्रेड का जीवनकाल
unit = TimeUnit.SECONDS,
workQueue = LinkedBlockingQueue<Runnable>(16),
threadFactory = Executors.defaultThreadFactory(),
handler = ThreadPoolExecutor.CallerRunsPolicy()
)
// कार्य सबमिट करना
threadPool.execute {
val result = api.fetchData()
runOnUiThread { showData(result) }
}
// पूल बंद करना
threadPool.shutdown()
// सभी कार्यों के पूरा होने की प्रतीक्षा
threadPool.awaitTermination(10, TimeUnit.SECONDS)
Android java.util.concurrent के माध्यम से थ्रेड पूल के कई कार्यान्वयन प्रदान करता है। Executors — तैयार कॉन्फ़िगरेशन वाला एक फैक्टरी: newFixedThreadPool(n) (निश्चित पूल), newCachedThreadPool() (असीमित, आवश्यकतानुसार थ्रेड बनाए जाते हैं), newSingleThreadExecutor() (एक थ्रेड — अनुक्रमिक निष्पादन)। मोबाइल प्रोजेक्ट्स के लिए, उचित सीमा (2-4 थ्रेड) के साथ newFixedThreadPool की अनुशंसा की जाती है, क्योंकि कैश्ड पूल बहुत अधिक थ्रेड बना सकता है।
ThreadPoolExecutor (TPE) — कॉन्फ़िगर करने योग्य पैरामीटरों के साथ ExecutorService का पूर्ण कार्यान्वयन। Android पर, TPE का उपयोग AsyncTask, IntentService और JobIntentService के अंदर किया जाता है। पैरामीटर corePoolSize, maximumPoolSize, keepAliveTime, BlockingQueue और RejectedExecutionHandler पूल व्यवहार को बारीकी से ट्यून करने की अनुमति देते हैं। Android के लिए सिफारिशें: corePoolSize = CPU कोर की संख्या - 1 (IO-bound कार्यों के लिए) या कोर की संख्या (CPU-bound कार्यों के लिए)। सामान्य एप्लिकेशन के लिए: 2-4 थ्रेड।
// Executors की तैयार कॉन्फ़िगरेशन
// 1. 3 थ्रेड का निश्चित पूल
val fixedPool = Executors.newFixedThreadPool(3)
// 2. कैश्ड पूल (मोबाइल के लिए अनुशंसित नहीं)
val cachedPool = Executors.newCachedThreadPool()
// 3. एकल थ्रेड (सीरियलाइज़ेशन)
val singlePool = Executors.newSingleThreadExecutor()
// 4. शेड्यूलर (आवधिक कार्य)
val scheduler = Executors.newScheduledThreadPool(2)
// Callable और Future के साथ उपयोग
val future: Future<String> = fixedPool.submit(Callable {
"Result: ${api.call()}"
})
// परिणाम प्राप्त करना (थ्रेड को ब्लॉक करता है)
val result = future.get(5, TimeUnit.SECONDS)
// पूल बंद करना
fixedPool.shutdownNow()
Kotlin कोरूटीन CoroutineDispatcher प्रदान करते हैं — थ्रेड पूल के समान एक अब्स्ट्रैक्शन। Dispatchers.IO 64 थ्रेड्स (सीमित) का पूल उपयोग करता है। Dispatchers.Default — CPU कोर की संख्या के बराबर पूल। CoroutineDispatcher को मैन्युअल shutdown की आवश्यकता नहीं है और यह स्वचालित रूप से प्रबंधित होता है। बारीक ट्यूनिंग के लिए, Executors.newFixedThreadPool(2).asCoroutineDispatcher() के माध्यम से कस्टम ExecutorCoroutineDispatcher बनाएँ। कोरूटीन थ्रेड पूल को प्रतिस्थापित नहीं करते, बल्कि उसे लपेटते हैं।
iOS थ्रेड पूल प्रबंधन के लिए दो मुख्य तंत्र प्रदान करता है: OperationQueue (GCD पर निर्मित उच्च-स्तरीय API) और GCD DispatchQueue (निम्न-स्तरीय C-API)। OperationQueue maxConcurrentOperationCount प्रॉपर्टी के माध्यम से थ्रेड पूल को एनकैप्सुलेट करता है। डिफ़ॉल्ट रूप से, OperationQueue सिस्टम-परिभाषित अधिकतम का उपयोग करता है (सिस्टम लोड पर निर्भर करता है)। DispatchQueue.global() सिस्टम थ्रेड पूल के साथ एक concurrent कतार प्रदान करता है।
OperationQueue maxConcurrentOperationCount के माध्यम से थ्रेड पूल का प्रबंधन करता है। मान 1 एक सीरियल कतार बनाता है (एकल थ्रेड पूल के समान)। 1 से अधिक मान निर्दिष्ट सीमा के साथ एक concurrent पूल बनाता है। डिफ़ॉल्ट रूप से, maxConcurrentOperationCount = NSOperationQueueDefaultMaxConcurrentOperationCount (सिस्टम ऑप्टिमम, आमतौर पर 4-8 थ्रेड)। Operation निर्भरता, प्राथमिकताएँ और रद्दीकरण का समर्थन करता है। प्रत्येक ऑपरेशन सिस्टम पूल के किसी भी उपलब्ध थ्रेड पर निष्पादित होता है।
// 3 थ्रेड के पूल के साथ OperationQueue
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 3
queue.qualityOfService = .utility
// ऑपरेशन बनाना
let operation1 = BlockOperation {
let data = fetchData(from: url1)
DispatchQueue.main.async { updateUI(data) }
}
let operation2 = BlockOperation {
let data = fetchData(from: url2)
DispatchQueue.main.async { updateUI(data) }
}
// निर्भरता: operation2, operation1 की प्रतीक्षा करता है
operation2.addDependency(operation1)
// कतार में जोड़ना
queue.addOperations([operation1, operation2], waitUntilFinished: false)
// सभी ऑपरेशन रद्द करना
queue.cancelAllOperations()
DispatchQueue — Apple का थ्रेड पूल है। एक concurrent कतार (qos: .utility) सिस्टम थ्रेड पूल का उपयोग करती है, जो वर्तमान डिवाइस लोड के लिए ऑप्टिमाइज़्ड है। विभिन्न QoS स्तर (userInteractive, userInitiated, utility, background) विभिन्न प्राथमिकताओं वाले विभिन्न पूलों पर मैप होते हैं। DispatchGroup कई कार्यों को सिंक्रोनाइज़ करने की अनुमति देता है। DispatchWorkItem रद्दीकरण और qualityOfService का समर्थन करता है। बारीक नियंत्रण के लिए, DispatchQueue(label: qos: attributes: .concurrent) के माध्यम से कस्टम concurrent कतारें बनाएँ।
// GCD DispatchQueue थ्रेड पूल के रूप में
let customQueue = DispatchQueue(
label: "com.app.background",
qos: .utility,
attributes: .concurrent,
autoreleaseFrequency: .workItem
)
// पूल में कार्य सबमिट करना
customQueue.async { self.processFile(file1) }
customQueue.async { self.processFile(file2) }
// सिंक्रोनाइज़ेशन के लिए DispatchGroup
let group = DispatchGroup()
let pool = DispatchQueue.global(qos: .utility)
pool.async(group: group) { fetchData() }
pool.async(group: group) { processImage() }
group.notify(queue: .main) {
self.showResult() // दोनों कार्य पूर्ण
}
// semaphore के माध्यम से concurrency सीमित करना
let semaphore = DispatchSemaphore(value: 3)
for url in urls {
pool.async {
semaphore.wait()
download(url)
semaphore.signal()
}
}
थ्रेड पूल कॉन्फ़िगरेशन सीधे एप्लिकेशन प्रदर्शन को प्रभावित करता है। गलत पैरामीटर CPU के कम उपयोग (बहुत कम थ्रेड) या सिस्टम ओवरलोड (बहुत अधिक) की ओर ले जाते हैं। मोबाइल एप्लिकेशन के लिए, सीमित संसाधनों और ऊर्जा खपत के कारण ऑप्टिमल मान सर्वर-साइड से भिन्न होते हैं। मुख्य पैरामीटर हैं: corePoolSize, maxPoolSize, कतार क्षमता और keepAliveTime।
IO-bound कार्यों के लिए सूत्र: corePoolSize = CPU कोर की संख्या × 2 (थ्रेड I/O की प्रतीक्षा करते हैं)। CPU-bound कार्यों के लिए: corePoolSize = CPU कोर की संख्या (थ्रेड लगातार गणनाओं में व्यस्त रहते हैं)। आधुनिक मोबाइल उपकरणों (6-8 कोर) पर यह CPU-bound के लिए 6-8 थ्रेड और IO-bound के लिए 12-16 देता है। व्यावहारिक परीक्षण दिखाते हैं कि सामान्य मोबाइल एप्लिकेशन के लिए 3-4 थ्रेड ऑप्टिमल हैं — अधिक थ्रेड प्रदर्शन लाभ के बिना ऊर्जा खपत बढ़ाते हैं।
कार्य कतार (work queue) का आकार निर्धारित करता है कि कितने कार्य निष्पादन की प्रतीक्षा कर सकते हैं। असीमित कतार (बिना सीमा के LinkedBlockingQueue) तेज़ कार्य आगमन पर OOM का कारण बन सकती है। सीमित कतार (निश्चित आकार वाली ArrayBlockingQueue) भरने पर कार्यों को अस्वीकार करती है। मोबाइल एप्लिकेशन के लिए, 16-32 कार्यों की क्षमता वाली ArrayBlockingQueue की अनुशंसा की जाती है। CallerRunsPolicy मोबाइल के लिए सबसे अच्छा RejectedExecutionHandler है: यह कार्य खोने के बजाय भेजने वाले को धीमा करता है (बैकप्रेशर)।
| पैरामीटर | मोबाइल के लिए सिफारिश | तर्क |
|---|---|---|
| corePoolSize | 2-4 | मोबाइल डिवाइस के सीमित संसाधन |
| maxPoolSize | corePoolSize (या +1-2) | थ्रेड निर्माण से पीक लोड से बचें |
| keepAliveTime | 15-30 सेकंड | बार-बार निर्माण के बिना तेज़ मेमोरी रिलीज़ |
| कतार क्षमता | 16-32 | बफ़रिंग और OOM जोखिम के बीच संतुलन |
| हैंडलर | CallerRunsPolicy | कार्य खोए बिना बैकप्रेशर |
मोबाइल एप्लिकेशन डेवलपर अक्सर थ्रेड पूल का उपयोग करते समय गलतियाँ करते हैं जो क्रैश, मेमोरी लीक और अस्थिर संचालन का कारण बनती हैं। सबसे सामान्य: ExecutorService के लिए shutdown() न कॉल करना, प्रत्येक ऑपरेशन के लिए नया पूल बनाना, बहुत बड़ा पूल, कार्यों के बीच deadlock, Android पर CachedThreadPool का उपयोग करना।
Deadlock तब होता है जब पूल में कोई कार्य उसी पूल के दूसरे कार्य के परिणाम की प्रतीक्षा करता है, लेकिन सभी थ्रेड प्रतीक्षा में व्यस्त हैं। उदाहरण: कार्य A उसी पूल में कार्य B भेजता है और future.get() कॉल करता है — यदि पूल समाप्त हो गया है, कार्य A कार्य B की प्रतीक्षा करता है, और कार्य B निष्पादित नहीं हो सकता क्योंकि कोई खाली थ्रेड नहीं है। समाधान: विभिन्न स्तरों के कार्यों के लिए अलग-अलग पूल का उपयोग करें या ब्लॉकिंग .get() के बजाय async callback का उपयोग करें।
// Thread Pool में Deadlock
val pool = Executors.newFixedThreadPool(1)
// कार्य A, कार्य B की प्रतीक्षा करता है — deadlock!
val futureA = pool.submit {
// यह कार्य कभी निष्पादित नहीं होगा
val futureB = pool.submit { 42 }
futureB.get() // हमेशा के लिए ब्लॉक हो जाता है
}
// समाधान: अलग पूल
val workerPool = Executors.newFixedThreadPool(2)
val callbackPool = Executors.newSingleThreadExecutor()
workerPool.submit {
callbackPool.submit {
// अलग पूल में चल रहा है — deadlock असंभव
}
}
// या CompletableFuture का उपयोग करें
workerPool.submit {
CompletableFuture
.supplyAsync { 42 }
.thenAccept { result ->
println(result)
}
}
Activity में बनाया गया ExecutorService onDestroy() में बंद किया जाना चाहिए। यदि ऐसा नहीं किया जाता है, तो Activity नष्ट होने के बाद भी थ्रेड मेमोरी में लटके रहेंगे। समाधान: Activity के बजाय पूल को Application स्कोप या ViewModel में रखें। कोरूटीन के लिए, viewModelScope या lifecycleScope का उपयोग करें। यदि पूल Activity के अंदर बनाया गया है, तो onDestroy() में pool.shutdown() कॉल करना सुनिश्चित करें। परीक्षण के लिए, तत्काल रोक के लिए es.shutdownNow() का उपयोग करें।
अक्सर पूछे जाने वाले प्रश्न
Thread Pool कई कार्यों को निष्पादित करने के लिए पहले से बनाए गए थ्रेड का पुनः उपयोग करता है। एक सामान्य थ्रेड (raw Thread) बनाया जाता है, एक कार्य निष्पादित करता है और नष्ट हो जाता है। थ्रेड बनाने में लगभग 1 MB मेमोरी और ~1 ms समय लगता है। Thread Pool ओवरहेड को कम करता है, थ्रेड की अधिकतम संख्या को सीमित करता है और प्रबंधन के लिए API (shutdown, awaitTermination) प्रदान करता है।
सामान्य मोबाइल एप्लिकेशन के लिए, 2-4 थ्रेड ऑप्टिमल हैं। CPU-bound कार्यों के लिए — CPU कोर की संख्या। IO-bound कार्यों के लिए — कोर की संख्या × 2। अधिक थ्रेड प्रदर्शन लाभ के बिना ऊर्जा खपत और संदर्भ स्विचिंग बढ़ाते हैं। Android पर, कोर निर्धारित करने के लिए Process.availableProcessors() का उपयोग करें। iOS पर — ProcessInfo.processInfo.processorCount।
CachedThreadPool आवश्यकतानुसार थ्रेड बनाता है और मौजूदा थ्रेड का पुनः उपयोग करता है। समस्या: यह थ्रेड की अधिकतम संख्या को सीमित नहीं करता है। यदि एक साथ 100 कार्य आते हैं, तो 100 थ्रेड बनाए जाएँगे। यह Android पर OOM का कारण बनता है (प्रत्येक थ्रेड ~1 MB)। स्पष्ट सीमा के साथ newFixedThreadPool(n) का उपयोग करें। CachedThreadPool केवल गारंटीकृत छोटी मात्रा वाले अल्पकालिक burst कार्यों के लिए स्वीकार्य है।
हाँ, यदि पूल प्रबंधित कंटेनर (जैसे कोरूटीन) से संबंधित नहीं है। shutdown() नए कार्य स्वीकार करना बंद कर देता है और वर्तमान कार्यों को पूरा करने के बाद थ्रेड समाप्त कर देता है। shutdown() के बिना, थ्रेड मेमोरी में लटके रहते हैं और एप्लिकेशन समाप्त नहीं होता है। Activity के लिए, इसे onDestroy() में कॉल करें। ViewModel के लिए, coroutineScope का उपयोग करें। पूल बंद करना संसाधन प्रबंधन का एक अनिवार्य हिस्सा है, जो Cursor या InputStream बंद करने के समान है।
हाँ, OperationQueue और DispatchQueue iOS द्वारा प्रदान किए गए थ्रेड पूल हैं। OperationQueue maxConcurrentOperationCount के माध्यम से concurrency को सीमित करता है। DispatchQueue.global() बिना सीधे नियंत्रण के सिस्टम थ्रेड पूल का उपयोग करता है। Java ThreadPoolExecutor के विपरीत, आप corePoolSize या कतार क्षमता का प्रबंधन नहीं करते — सिस्टम वर्तमान लोड और डिवाइस ऊर्जा खपत के आधार पर पूल को स्वचालित रूप से ऑप्टिमाइज़ करता है।
सारांश
हम एक मोबाइल एप्लिकेशन टर्नकी विकसित करेंगे
IT Sectr 2017 से स्टार्टअप और व्यवसायों के लिए iOS और Android एप्लिकेशन बनाता है। हम आपको सलाह देंगे और सर्वोत्तम समाधान प्रस्तावित करेंगे।
यह भी पढ़ें