Thread — प्रोसेसर समय की मूल इकाई है, जिसका अपना स्टैक होता है और यह अन्य थ्रेड से स्वतंत्र रूप से निष्पादित होता है। मोबाइल डेवलपमेंट में थ्रेड का उपयोग कार्यों को समानांतर रूप से निष्पादित करने के लिए किया जाता है, ताकि लंबे संचालन के दौरान इंटरफ़ेस प्रतिक्रियाशील बना रहे। Android java.lang.Thread, Executors और Kotlin Coroutines को सपोर्ट करता है, iOS — Thread (Objective-C), GCD और OperationQueue को। Android Thread Documentation के अनुसार, नेटिव थ्रेड बनाने के लिए ऑपरेटिंग सिस्टम द्वारा स्टैक के लिए ~1 MB आवंटित करने की आवश्यकता होती है।
मुख्य बातें
Thread (निष्पादन थ्रेड) — निर्देशों का एक स्वतंत्र अनुक्रम है, जिसे ऑपरेटिंग सिस्टम CPU कोर पर शेड्यूल कर सकता है। प्रत्येक प्रक्रिया (एप्लिकेशन) में कम से कम एक थ्रेड — Main Thread होता है। अतिरिक्त थ्रेड समानांतर कार्य निष्पादन के लिए बनाए जाते हैं। प्रत्येक थ्रेड का अपना प्रोग्राम स्टैक (स्थानीय चर के साथ), इंस्ट्रक्शन काउंटर (PC) और रजिस्टर होते हैं। हीप मेमोरी प्रक्रिया के सभी थ्रेड के लिए साझा होती है।
मोबाइल OS में थ्रेड प्रीएम्प्टिव मल्टीटास्किंग (preemptive multitasking) द्वारा शेड्यूल किए जाते हैं: OS किसी भी समय थ्रेड के निष्पादन को बाधित कर सकता है और दूसरे को नियंत्रण सौंप सकता है (context switch)। कॉन्टेक्स्ट स्विच एक महंगा संचालन है (1-10 माइक्रोसेकंड), क्योंकि इसमें CPU रजिस्टरों को सहेजना/पुनर्स्थापित करना, TLB को अपडेट करना और कैश को फ्लश करना शामिल है। यही कारण है कि अत्यधिक संख्या में थ्रेड (सैकड़ों और हजारों) प्रदर्शन को खराब करते हैं — OS निष्पादन की तुलना में स्विचिंग पर अधिक समय खर्च करता है।
थ्रेड और प्रक्रिया — अलग-अलग अवधारणाएँ हैं। प्रक्रिया — आवंटित वर्चुअल मेमोरी वाला एप्लिकेशन का एक इंस्टेंस है। प्रक्रिया के अंदर थ्रेड इस मेमोरी को अन्य थ्रेड के साथ साझा करता है। Android में एप्लिकेशन का प्रत्येक घटक (Activity, Service, BroadcastReceiver) एक प्रक्रिया में काम करता है, लेकिन विभिन्न थ्रेड में निष्पादित हो सकता है। iOS एप्लिकेशन भी GCD या Thread के माध्यम से अतिरिक्त थ्रेड बनाने की क्षमता वाली एक प्रक्रिया है।
प्रत्येक थ्रेड Java/Kotlin (Android) और NSThread (iOS) में पाँच अवस्थाओं से गुज़रता है: New (बनाया गया), Runnable (निष्पादन के लिए तैयार), Running (CPU पर निष्पादित), Blocked/Waiting (संसाधन या सूचना की प्रतीक्षा), Terminated (समाप्त)। अवस्थाओं के बीच संक्रमण OS शेड्यूलर और सिंक्रनाइज़ेशन प्रिमिटिव द्वारा प्रबंधित किया जाता है। डेवलपर थ्रेड प्राथमिकता (Thread.setPriority()) और इसकी स्थिति (sleep, join, interrupt) को प्रभावित कर सकता है।
Android में थ्रेड Blocked अवस्था में तब जाता है जब व्यस्त मॉनिटर (synchronized) को पकड़ने का प्रयास करता है, Object.wait() या Thread.sleep() कॉल करता है। iOS में — NSCondition.wait(), pthread_cond_wait() या dispatch_semaphore_wait() कॉल करने पर। Blocked अवस्था में थ्रेड CPU का उपभोग नहीं करता, लेकिन मेमोरी (स्टैक) घेरता है। थ्रेड को दूसरे थ्रेड से बाधित (interrupted) किया जा सकता है, जिससे InterruptedException (Java) या isCancelled (Kotlin Coroutines) मिलता है।
| अवस्था | विवरण | संक्रमण विधि |
|---|---|---|
| New | थ्रेड बनाया गया, लेकिन शुरू नहीं किया गया | Thread() कंस्ट्रक्टर |
| Runnable | थ्रेड निष्पादन के लिए तैयार, CPU की प्रतीक्षा | thread.start() |
| Running | थ्रेड CPU कोर पर निष्पादित | OS शेड्यूलर |
| Blocked/Waiting | थ्रेड संसाधन, मॉनिटर या सूचना की प्रतीक्षा | synchronized, wait(), sleep() |
| Terminated | थ्रेड ने run() पूरा किया या बाधित किया गया | run() पूर्ण, interrupt() |
Context switch (कॉन्टेक्स्ट स्विच) — वह संचालन जिसमें OS वर्तमान थ्रेड की स्थिति (रजिस्टर, PC, TLB) को सहेजता है और दूसरे की सहेजी गई स्थिति को लोड करता है। मोबाइल सिस्टम में (Linux + ART, iOS के लिए XNU) context switch में 1-10 माइक्रोसेकंड लगते हैं। यदि थ्रेड 100 माइक्रोसेकंड में कार्य करता है और context switch में 5 लगते हैं, तो 5% समय बर्बाद होता है। context switch को कम करने के लिए iOS work stealing के साथ GCD का उपयोग करता है, Android — fixedThreadCount वाले पूल का।
Android ने निम्न-स्तरीय java.lang.Thread से आधुनिक कोरूटीन तक का विकास किया है। एब्स्ट्रैक्शन का प्रत्येक स्तर कम ओवरहेड के साथ अधिक क्षमताएँ देता है। Thread — मूल वर्ग है, लेकिन इसका सीधा निर्माण अनुशंसित नहीं है: नया थ्रेड पूल द्वारा प्रबंधित नहीं होता, इसकी निगरानी और रद्द करना कठिन है। AsyncTask (API 30 से deprecated) एक कदम आगे था, लेकिन मेमोरी लीक और कॉन्फ़िगरेशन हैंडलिंग की असुविधा से ग्रस्त था।
HandlerThread — Looper के साथ Thread का एक विशेष उपवर्ग है, जो संदेश कतार को संसाधित कर सकता है। इसका उपयोग पृष्ठभूमि थ्रेड पर अनुक्रमिक कार्यों के लिए किया जाता है, जैसे Room या फ़ाइलों में डेटा लिखना। HandlerThread start() कॉल करके बनाया जाता है, जिसके बाद Handler(handlerThread.looper) के माध्यम से संदेश और Runnable भेजे जा सकते हैं। handlerThread.quit() कॉल Looper को रोकता है और थ्रेड को समाप्त करता है।
// Android: Thread, HandlerThread और Executors
import android.os.Handler
import android.os.HandlerThread
import java.util.concurrent.Executors
class ThreadExample {
// 1. सीधा Thread निर्माण (अनुशंसित नहीं)
fun directThread() {
val thread = Thread(Runnable {
Thread.sleep(1000)
print("Direct thread executed")
})
thread.start()
}
// 2. HandlerThread अनुक्रमिक पृष्ठभूमि कार्यों के लिए
fun handlerThreadExample() {
val handlerThread = HandlerThread("BackgroundQueue")
handlerThread.start()
val handler = Handler(handlerThread.looper)
handler.post {
// पृष्ठभूमि थ्रेड पर अनुक्रमिक निष्पादन
Thread.sleep(500)
print("HandlerThread: कार्य पूर्ण हुआ")
}
// थ्रेड को रोकना (जब कार्य समाप्त हो जाएँ तब निष्पादित होता है)
handlerThread.quitSafely()
}
// 3. Executors — थ्रेड पूल
fun executorExample() {
val executor = Executors.newFixedThreadPool(4)
for (i in 1..10) {
executor.execute {
print("Task $i on thread ${Thread.currentThread().getName()}")
}
}
executor.shutdown()
}
// 4. Kotlin Coroutines — आधुनिक मानक
suspend fun coroutineExample() = kotlinx.coroutines.withContext(
kotlinx.coroutines.Dispatchers.Default
) {
print("Coroutine on thread: ${Thread.currentThread().getName()}")
}
}
उदाहरण ThreadExample Android में थ्रेड एब्स्ट्रैक्शन के चारों स्तरों को दर्शाता है। सीधा Thread निर्माण — सबसे निम्न-स्तरीय और अकुशल दृष्टिकोण है। HandlerThread पृष्ठभूमि में अनुक्रमिक कार्यों के लिए उपयोगी है। Executors.newFixedThreadPool(4) 10 कार्यों के समानांतर निष्पादन के लिए 4 थ्रेड का पूल बनाता है। Kotlin Coroutines Dispatchers.Default के साथ — आधुनिक, कुशल और सुरक्षित तरीका है।
HandlerThread — अंतर्निर्मित Looper और संदेश कतार के साथ Thread का एक विशेष उपवर्ग है। यह start() कॉल करके बनाया जाता है, जिसके बाद Handler(handlerThread.looper) के माध्यम से Runnable और संदेश भेजे जा सकते हैं। HandlerThread कार्यों को सख्ती से अनुक्रमिक रूप से निष्पादित करता है — अगला कार्य पिछले के पूरा होने तक शुरू नहीं होता। यह Room या फ़ाइलों में डेटा लिखने के लिए सुविधाजनक है, जहाँ संचालन का क्रम महत्वपूर्ण है। quitSafely() कॉल वर्तमान कार्य पूरा होने के बाद Looper को रोकता है।
iOS भी थ्रेड के साथ काम करने के तीन स्तर प्रदान करता है। Thread (Swift में Thread, Objective-C में NSThread) — निम्न-स्तरीय API, जो सीधे नेटिव थ्रेड बनाता है। GCD (Grand Central Dispatch) DispatchQueue के माध्यम से — iOS डेवलपर्स के लिए मुख्य उपकरण, जो स्वचालित रूप से थ्रेड पूल का प्रबंधन करता है। OperationQueue — निर्भरता, प्राथमिकताएँ और रद्दीकरण के समर्थन के साथ GCD पर उच्च-स्तरीय एब्स्ट्रैक्शन है।
आधुनिक iOS डेवलपमेंट में Thread का सीधा उपयोग अत्यंत दुर्लभ है — GCD स्वचालित मेमोरी और थ्रेड प्रबंधन के साथ सभी आवश्यक क्षमताएँ प्रदान करता है। Thread का उपयोग केवल विशिष्ट मामलों के लिए किया जाता है: thread-local storage (threadDictionary) सेट करना, पृष्ठभूमि थ्रेड के लिए RunLoop बनाना, या C-लाइब्रेरी के साथ एकीकरण जो pthread_t की अपेक्षा करती हैं।
import Foundation
class ThreadManager {
// 1. Thread (निम्न-स्तरीय)
func createThread() {
let thread = Thread {
// कोड नए थ्रेड पर निष्पादित होता है
print("Current thread: \(Thread.current)")
}
thread.name = "com.app.worker"
thread.qualityOfService = .utility
thread.start()
}
// 2. GCD — DispatchQueue
func gcdExample() {
// समानांतर कतार
let queue = DispatchQueue(label: "com.app.concurrent",
qos: .utility,
attributes: .concurrent)
queue.async {
print("GCD async task")
}
// लिखाई सिंक्रनाइज़ेशन के लिए Barrier
queue.async(flags: .barrier) {
// लिखाई के दौरान अनन्य पहुँच
print("Barrier write: exclusive access")
}
}
// 3. निर्भरता के साथ OperationQueue
func operationQueueExample() {
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 2
queue.qualityOfService = .background
let download = BlockOperation {
print("Downloading...")
}
let process = BlockOperation {
print("Processing...")
}
let save = BlockOperation {
print("Saving...")
}
// निर्भरताएँ: download -> process -> save
process.addDependency(download)
save.addDependency(process)
queue.addOperations([download, process, save], waitUntilFinished: false)
}
}
// GCD barrier के माध्यम से Thread-safe संग्रह
class ThreadSafeArray<T> {
private var array: [T] = []
private let queue = DispatchQueue(label: "com.app.concurrent",
attributes: .concurrent)
var count: Int {
return queue.sync { array.count } // concurrent read
}
func append(_ element: T) {
queue.async(flags: .barrier) { // exclusive write
self.array.append(element)
}
}
}
वर्ग ThreadSafeArray GCD barrier के माध्यम से Concurrent Read / Exclusive Write पैटर्न प्रदर्शित करता है। queue.sync{} के माध्यम से पढ़ना कई थ्रेड से समानांतर रूप से निष्पादित होता है। queue.async(flags: .barrier) के माध्यम से लिखना लिखाई पूरी होने तक अन्य सभी संचालनों (पढ़ना और लिखना दोनों) को ब्लॉक करता है। यह synchronized-ब्लॉक से अधिक कुशल है, क्योंकि यह पाठकों को तब तक ब्लॉक नहीं करता जब तक लिखाई न हो।
iOS में सीधे Thread का उपयोग तीन मामलों में उचित है: thread-local storage (Thread.current.threadDictionary) के लिए — थ्रेड से जुड़े डेटा का भंडारण; पृष्ठभूमि थ्रेड पर performSelector:onThread: के साथ विशेष RunLoop बनाने के लिए; C/C++ लाइब्रेरी के साथ एकीकरण के लिए जो pthread_t की अपेक्षा करती हैं। अन्य सभी मामलों में GCD DispatchQueue के माध्यम से बेहतर है — यह स्वचालित रूप से थ्रेड पूल और ऊर्जा खपत का प्रबंधन करता है।
Race condition (दौड़ की स्थिति) तब उत्पन्न होती है जब दो या अधिक थ्रेड एक साथ साझा डेटा तक पहुँचते हैं, और कम से कम एक थ्रेड लिखता है। परिणाम निष्पादन के क्रम (timing) पर निर्भर करता है और अप्रत्याशित होता है। race condition को रोकने के लिए सिंक्रनाइज़ेशन प्रिमिटिव का उपयोग किया जाता है। मोबाइल डेवलपमेंट में लॉक (synchronized, NSLock), एटॉमिक ऑपरेशन (AtomicInteger, iOS atomic-गुण) और कतारें (serial queue) उपलब्ध हैं।
प्रिमिटिव का चयन परिदृश्य पर निर्भर करता है। सरल काउंटर और फ़्लैग के लिए एटॉमिक ऑपरेशन (AtomicInteger, atomic property) पर्याप्त हैं। कई संचालनों वाले क्रिटिकल सेक्शन के लिए — लॉक (synchronized, NSLock)। जटिल डेटा संरचनाओं के लिए — serial DispatchQueue या GCD barrier। लॉक समझने में आसान हैं, लेकिन deadlock और livelock के लिए संवेदनशील हैं। कतारें अधिक जटिल लेकिन सुरक्षित हैं।
// Android/Kotlin में सिंक्रनाइज़ेशन
import java.util.concurrent.atomic.AtomicInteger
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
class Counter {
// 1. AtomicInteger — सरल काउंटर के लिए
private val atomicCount = AtomicInteger(0)
fun incrementAtomic() = atomicCount.incrementAndGet()
// 2. synchronized — क्रिटिकल सेक्शन के लिए
@Synchronized
fun synchronizedOperation() {
// एक बार में केवल एक थ्रेड
doWork()
}
// 3. कोरूटीन से Mutex — suspend-safe
private val mutex = Mutex()
suspend fun mutexOperation() {
mutex.withLock {
// सुरक्षित कोड — थ्रेड-सुरक्षित
doWork()
}
}
private fun doWork() { /* critical section */ }
}
// Deadlock उदाहरण: A, B को लॉक करता है, B, A को लॉक करता है
class DeadlockExample {
private val lockA = Any()
private val lockB = Any()
fun methodA() = synchronized(lockA) {
Thread.sleep(100)
synchronized(lockB) { print("OK") }
}
fun methodB() = synchronized(lockB) {
Thread.sleep(100)
synchronized(lockA) { print("OK") }
}
}
Counter सिंक्रनाइज़ेशन के तीन दृष्टिकोण प्रदर्शित करता है। AtomicInteger.incrementAndGet() — बिना लॉक के एटॉमिक ऑपरेशन (CAS)। @Synchronized — Java का अंतर्निर्मित मॉनिटर, पूरे ऑब्जेक्ट को लॉक करता है। Mutex.withLock — कोरूटीन म्यूटेक्स, थ्रेड को ब्लॉक करने के बजाय कोरूटीन को रोकता है (अधिक कुशल)। DeadlockExample क्लासिक deadlock दिखाता है: दो थ्रेड अलग-अलग क्रम में लॉक पकड़ते हैं।
Thread Pool (थ्रेड पूल) — पहले से बनाए गए थ्रेड का एक सेट है, जो कार्यों को निष्पादित करने के लिए पुनः उपयोग किए जाते हैं। प्रत्येक कार्य के लिए नया थ्रेड बनाने (महंगा) के बजाय पूल पूल से एक खाली थ्रेड लेता है। यदि खाली थ्रेड नहीं हैं, तो कार्य कतार में रखा जाता है। पूल स्वचालित रूप से आकार प्रबंधित करता है: पीक लोड पर नए थ्रेड बनाए जाते हैं, निष्क्रिय थ्रेड समाप्त किए जाते हैं। इससे थ्रेड निर्माण पर ओवरहेड दसियों गुना कम हो जाता है।
Android में Executors.newFixedThreadPool(4) 4 थ्रेड का पूल बनाता है। यदि एक साथ 10 कार्य आते हैं, 4 तुरंत निष्पादित होने लगते हैं, 6 कतार में प्रतीक्षा करते हैं। Executors.newCachedThreadPool() आवश्यकता के अनुसार थ्रेड बनाता है (बिना सीमा के) और 60 सेकंड के बाद निष्क्रिय थ्रेड को समाप्त करता है। iOS के लिए GCD स्वचालित रूप से वैश्विक कतारों के पूल प्रदान करता है, जिनका आकार CPU कोर की संख्या और वर्तमान लोड के अनुरूप होता है।
Kotlin Coroutines में थ्रेड पूल डिस्पैचर के अंदर छिपे होते हैं। Dispatchers.Default CPU कोर की संख्या के बराबर आकार का पूल उपयोग करता है (न्यूनतम 2)। Dispatchers.IO — 64 थ्रेड (सैकड़ों IO-bound कार्यों के लिए पर्याप्त, क्योंकि अधिकांश इनपुट-आउटपुट की प्रतीक्षा करेंगे, CPU नहीं घेरेंगे)। प्रत्येक डिस्पैचर स्वचालित रूप से लोड के अनुसार पूल को स्केल करता है, निष्क्रियता पर बैटरी ऊर्जा बचाता है।
अक्सर पूछे जाने वाले प्रश्न
Thread — एप्लिकेशन में कोड निष्पादन की मूल इकाई है। प्रत्येक प्रक्रिया में कई थ्रेड हो सकते हैं, जो मेमोरी साझा करते हैं लेकिन अपने स्वयं के स्टैक के साथ। मोबाइल डेवलपमेंट में थ्रेड का उपयोग UI को ब्लॉक किए बिना समानांतर कार्य निष्पादन के लिए किया जाता है। Android Thread, Executors, HandlerThread और Coroutines का उपयोग करता है। iOS Thread, GCD (DispatchQueue) और OperationQueue का उपयोग करता है।
Thread निर्माण में Android पर ~1 MB और iOS पर ~512 KB स्टैक आवंटन की आवश्यकता होती है — यह एक महंगा संचालन है। 1000 कार्यों के लिए 1000 थ्रेड का सीधा निर्माण केवल स्टैक के लिए ~1 GB की आवश्यकता होगी, साथ ही context switch का ओवरहेड। Thread के बजाय पूल (Executors, GCD) या कोरूटीन का उपयोग करें — वे थ्रेड का पुनः उपयोग करते हैं, जिससे ओवरहेड दसियों गुना कम हो जाता है।
Race condition — कई थ्रेड द्वारा लिखने के साथ एक साथ साझा डेटा तक पहुँचने पर अप्रत्याशित व्यवहार है। तीन तरीकों से बचा जा सकता है: एटॉमिक प्रकार (AtomicInteger), लॉक (synchronized, NSLock) का उपयोग करना, या कतार (DispatchQueue serial, Kotlin में Actor) के माध्यम से पहुँच को क्रमबद्ध करना। सबसे अच्छी प्रथा — साझा mutable स्थिति को कम करना और immutability का उपयोग करना है।
Thread — मूल सिस्टम ऑब्जेक्ट है, जो ~1 MB स्टैक घेरता है और OS कोर से बंधा होता है। कोरूटीन — Kotlin की हल्की निष्पादन इकाई है, जो किसी विशिष्ट थ्रेड से बंधी नहीं होती और बिना ब्लॉकिंग के रुक (suspend) सकती है। एक थ्रेड हजारों कोरूटीन निष्पादित कर सकता है। कोरूटीन मेमोरी में अधिक कुशल हैं और बिना callbacks के एसिंक्रोनस कोड लिखने की अनुमति देते हैं।
Deadlock ANR के बिना एप्लिकेशन के पूर्ण हैंग के रूप में प्रकट होता है। Android में Thread.getAllStackTraces() का उपयोग करें — दो थ्रेड एक-दूसरे के लॉक की प्रतीक्षा कर रहे होंगे। iOS में — Thread.callStackSymbols। उपकरण: Android Studio Profiler (Threads tab), Instruments (iOS, Thread State View)। रोकथाम: लॉक को निश्चित क्रम में पकड़ें, टाइमआउट के साथ tryLock का उपयोग करें।
निष्कर्ष
हम एक मोबाइल एप्लिकेशन टर्नकी विकसित करेंगे
IT Sectr 2017 से स्टार्टअप और व्यवसायों के लिए iOS और Android एप्लिकेशन बनाता है। हम आपको सलाह देंगे और सर्वोत्तम समाधान प्रस्तावित करेंगे।
यह भी पढ़ें