Livelock (सक्रिय लॉक) मल्टीथ्रेडेड प्रोग्रामिंग में एक स्थिति है जहाँ थ्रेड ब्लॉक नहीं होते हैं लेकिन एक-दूसरे की क्रियाओं पर अंतहीन प्रतिक्रिया करते हैं, बिना कोई उपयोगी कार्य किए। Baeldung (Java Concurrency Guide, 2024) के अनुसार, Livelock में थ्रेड पड़ोसी थ्रेड्स की स्थिति के जवाब में लगातार स्थिति बदलते हैं, लेकिन कोई भी अपने लक्ष्य तक नहीं पहुँचता। Deadlock के विपरीत, Livelock 100% CPU की खपत करता है, जो मोबाइल डिवाइस की बैटरी को जल्दी खत्म कर देता है।
मुख्य बातें
Livelock (सक्रिय लॉक) एक मल्टीथ्रेडेड सिस्टम में ऐसी स्थिति है जहाँ थ्रेड ब्लॉक नहीं होते हैं लेकिन कोई उपयोगी कार्य भी नहीं करते हैं। प्रत्येक थ्रेड पता लगाता है कि वह जारी नहीं रख सकता और इसे ठीक करने का प्रयास करता है, लेकिन उसकी क्रियाएँ अन्य थ्रेड्स में समान प्रतिक्रिया उत्पन्न करती हैं। परिणामस्वरूप, सिस्टम बिना कोई प्रगति किए अंतहीन रूप से स्थितियों के बीच स्विच करता है।
Livelock का एक उत्कृष्ट सादृश्य दो लोग एक संकीर्ण गलियारे में मिलते हैं। प्रत्येक दूसरे को रास्ता देने के लिए एक तरफ हटने की कोशिश करता है, लेकिन दोनों एक साथ एक ही हरकत करते हैं और फिर से एक-दूसरे के सामने आ जाते हैं। वे स्थिर नहीं खड़े हैं (वह Deadlock होता), बल्कि सक्रिय रूप से चल रहे हैं, लेकिन कभी अलग नहीं हो पाते। प्रोग्रामिंग में, यह थ्रेड्स के लगातार संसाधनों को मुक्त करने और पुनः अधिग्रहण करने के अनुरूप है।
मोबाइल डेवलपमेंट में, Livelock विशेष रूप से खतरनाक है क्योंकि यह उपयोगकर्ता के लिए अदृश्य है: ऐप फ्रीज़ नहीं होता, इंटरफ़ेस ब्लॉक नहीं होता, लेकिन पृष्ठभूमि थ्रेड्स द्वारा 100% CPU लोड के कारण बैटरी 2-3 गुना तेजी से खत्म होती है। Google परीक्षणों (Android Battery Optimization, 2023) के अनुसार, पृष्ठभूमि Service में Livelock डिवाइस की बैटरी लाइफ को 40% तक कम कर सकता है।
Livelock तब उत्पन्न होता है जब कई थ्रेड संघर्ष पर समान प्रतिक्रिया रणनीति का उपयोग करते हैं। यदि थ्रेड A एक संसाधन प्राप्त नहीं कर सकता और अपना वर्तमान संसाधन मुक्त करता है, जबकि थ्रेड B एक ही समय में ऐसा ही करता है, तो दोनों चक्र दोहराते हैं — और स्थिति अंतहीन रूप से दोहराई जाती है। यह विशेष रूप से TryLock और विफलता पर स्वचालित मुक्ति वाले एल्गोरिदम की विशेषता है।
जब थ्रेड पुनः प्रयास से पहले निश्चित विलंब का उपयोग करते हैं, तो वे एक सिंक्रोनस चक्र में प्रवेश कर सकते हैं। यदि दोनों थ्रेड समान समय प्रतीक्षा करते हैं, तो वे फिर से एक साथ संसाधन प्राप्त करने का प्रयास करेंगे और फिर से एक साथ इसे मुक्त करेंगे। समस्या यादृच्छिक घटक (jitter) के साथ exponential backoff का उपयोग करके हल की जाती है, जैसे Ethernet में CSMA/CD एल्गोरिदम में।
मोबाइल डेवलपमेंट में, Livelock अक्सर कार्य कतारों के गलत कार्यान्वयन के कारण उत्पन्न होता है। उदाहरण के लिए, जब एक वर्कर थ्रेड एक संदेश को संसाधित करना समाप्त करता है लेकिन प्राथमिकता तर्क के कारण लगातार नियंत्रण दूसरे वर्कर को स्थानांतरित करता है जो वही करता है। ऐसी स्थितियाँ गैर-मानक RejectedExecutionHandler नीतियों वाले कस्टम ThreadPoolExecutors के लिए विशिष्ट हैं।
एक स्थिति पर विचार करें जहाँ दो थ्रेड TryLock का उपयोग करते हैं और विफलता पर संसाधन मुक्त करते हैं। सक्रिय लॉक उत्पन्न होता है क्योंकि दोनों थ्रेड समान तर्क लागू करते हैं और सिंक्रोनस रूप से पुनः प्रयास करते हैं।
import java.util.concurrent.locks.ReentrantLock
import java.util.concurrent.TimeUnit
class LivelockWorker(private val name: String,
private val lock1: ReentrantLock,
private val lock2: ReentrantLock) {
fun execute() {
while (true) {
if (lock1.tryLock(50, TimeUnit.MILLISECONDS)) {
if (lock2.tryLock(50, TimeUnit.MILLISECONDS)) {
println("$name — पूर्ण हुआ!")
lock2.unlock()
lock1.unlock()
return
} else {
lock1.unlock() // मुक्त करें और पुनः प्रयास करें
}
}
Thread.sleep(50) // समान विलंब — Livelock का मुख्य कारक
}
}
}
यदि LivelockWorker के दो उदाहरण lock1 और lock2 प्राप्त करने के विभिन्न क्रम के साथ चलाए जाते हैं, तो वे सक्रिय लॉक में प्रवेश करेंगे। प्रत्येक पहला संसाधन प्राप्त करेगा, दूसरा नहीं मिलेगा, पहले को मुक्त करेगा, 50 ms प्रतीक्षा करेगा और पुनः प्रयास करेगा — अंतहीन रूप से, CPU की खपत करता हुआ। समाधान विलंब में यादृच्छिक घटक (jitter) जोड़ना और पुनः प्रयासों की संख्या सीमित करना है।
सही किया गया संस्करण यादृच्छिक jitter के साथ exponential backoff का उपयोग करता है। प्रत्येक असफल प्रयास के बाद, प्रतीक्षा समय एक यादृच्छिक गुणक के साथ बढ़ता है, जो थ्रेड्स के बीच तालमेल को तोड़ता है।
fun executeWithBackoff() {
var delay = 10L
var attempts = 0
while (attempts < 5) {
if (lock1.tryLock(delay, TimeUnit.MILLISECONDS)) {
if (lock2.tryLock(delay, TimeUnit.MILLISECONDS)) {
println("सफलता!")
lock2.unlock(); lock1.unlock()
return
}
lock1.unlock()
}
delay = (delay * 2 + (0..50).random())
attempts++
}
println("5 प्रयासों के बाद विफल")
}
बाहरी समानता के बावजूद, Livelock और Deadlock में मौलिक रूप से भिन्न तंत्र और परिणाम हैं। Deadlock में, थ्रेड ब्लॉक होते हैं और CPU की खपत नहीं करते — एप्लिकेशन बस फ्रीज़ हो जाता है। Livelock में, थ्रेड सक्रिय हैं, 100% CPU की खपत करते हैं, लेकिन कोई उपयोगी कार्य नहीं करते। समाधान रणनीति का चुनाव लॉक के प्रकार की सही पहचान पर निर्भर करता है।
| पैरामीटर | Deadlock | Livelock |
|---|---|---|
| थ्रेड की स्थिति | BLOCKED / WAITING | RUNNABLE |
| CPU खपत | न्यूनतम | उच्च (90-100%) |
| बैटरी खपत | कम | उच्च |
| पता लगाना | Thread Dump | CPU Profiler + दृश्य विश्लेषण |
| सामान्य कारण | लॉक प्राप्त करने का अलग क्रम | संघर्ष पर समान प्रतिक्रिया रणनीति |
| समाधान | लॉक पदानुक्रम | Retry limit + exponential backoff |
मोबाइल डेवलपमेंट में, व्यावहारिक अंतर बहुत बड़ा है। Deadlock ANR और ऐप पुनरारंभ की ओर ले जाता है — इसका पता लगाया जाता है और Google Play Console के माध्यम से रिपोर्ट किया जाता है। Livelock किसी का ध्यान नहीं जाता: ऐप काम करता हुआ दिखता है, लेकिन बैटरी एक घंटे में खत्म हो जाती है, और उपयोगकर्ता बस ऐप हटा देता है। Firebase Analytics (App Retention Report, 2024) के अनुसार, 68% उपयोगकर्ता ऐप हटा देते हैं यदि वह पृष्ठभूमि में अत्यधिक बैटरी खर्च करता है।
Livelock का पता लगाना Deadlock से अधिक कठिन है क्योंकि सिस्टम कोई स्पष्ट संकेत नहीं देता — कोई अपवाद नहीं, कोई ANR नहीं, कोई त्रुटि संदेश नहीं। मुख्य निदान विधि Android Studio में CPU Profiler है। यदि कोई थ्रेड लगातार RUNNABLE स्थिति में है लेकिन कोई उपयोगी I/O या गणना नहीं कर रहा है — तो यह Livelock का संदेह है।
एक अतिरिक्त संकेतक है ऐप के निष्क्रिय होने पर असामान्य बैटरी खपत। Android Battery Historian (Android SDK का एक उपकरण) घटकों द्वारा ऊर्जा खपत ग्राफ बनाता है। यदि CPU Wakelock बिना स्पष्ट कारण के बना रहता है — तो Method Tracing चलाएँ और संदिग्ध थ्रेड्स के कॉल स्टैक का विश्लेषण करें।
कोड स्तर पर, पुनः प्रयास प्रयासों की लॉगिंग threadId और समय के साथ मदद करती है। यदि लॉग एक सेकंड में हजारों पुनः प्रयास दिखाता है बिना एक भी सफलता के — यह Livelock है। Hystrix-जैसे circuit breaker या retry काउंटर को एक सीमा के साथ लागू करने की सिफारिश की जाती है, जिसे पार करने पर ऑपरेशन अक्षम हो जाता है और Crashlytics के माध्यम से डेवलपर को सूचित करता है।
सबसे सरल और सबसे विश्वसनीय तरीका है प्रयासों की संख्या को सीमित करना संसाधन प्राप्त करने के लिए। यदि N प्रयासों के बाद ऑपरेशन विफल होता है, तो थ्रेड त्रुटि स्थिति में जाता है और उपयोगकर्ता को सूचित करता है। N अनुभवजन्य रूप से चुना जाता है: मोबाइल ऐप्स के लिए, आमतौर पर 3-5 प्रयास। यह उच्च लोड के तहत दुर्लभ गलत सकारात्मकता की कीमत पर अनंत Livelock को पूरी तरह से समाप्त करता है।
प्रयासों के बीच निश्चित विलंब के बजाय, एक तेजी से बढ़ता विराम यादृच्छिक घटक के साथ उपयोग किया जाता है। सूत्र: delay = min(baseDelay * 2^attempt, maxDelay) + random(0, jitter)। यह दृष्टिकोण न केवल थ्रेड्स की तालमेल को तोड़ता है बल्कि उच्च प्रतिस्पर्धा के तहत सिस्टम पर समग्र भार को भी कम करता है। इसका उपयोग नेटवर्क प्रोटोकॉल एल्गोरिदम में किया जाता है और Firebase Realtime Database retry logic के लिए Google द्वारा अनुशंसित है।
विभिन्न थ्रेड्स को अलग-अलग रणनीतियाँ निर्दिष्ट करना Livelock के मूल कारण को समाप्त करता है — संघर्ष पर समान प्रतिक्रिया। उदाहरण के लिए, उच्च-प्राथमिकता वाला थ्रेड बिना मुक्त किए संसाधन प्राप्त करता है, जबकि निम्न-प्राथमिकता वाला मुक्त करता है और प्रतीक्षा करता है। मोबाइल डेवलपमेंट में, UI थ्रेड को लॉक प्राप्त करने में प्राथमिकता मिल सकती है, जबकि पृष्ठभूमि वर्कर थ्रेड टाइमआउट के साथ TryLock का उपयोग करते हैं।
कुछ आर्किटेक्चर में, Livelock डिज़ाइन स्तर पर रोका जाता है: केवल एक दिशा में संसाधनों की मुक्ति। उदाहरण के लिए, यदि थ्रेड A हमेशा एक निश्चित चैनल के माध्यम से नियंत्रण थ्रेड B को स्थानांतरित करता है, और B कभी A को नियंत्रण वापस करने का प्रयास नहीं करता — प्रतिक्रिया चक्र असंभव है। एकदिशीय प्रसंस्करण चरणों वाली Pipeline आर्किटेक्चर Android CameraX और MediaPipe में आसन्न चरणों के बीच Livelock को पूरी तरह से समाप्त करती है।
अक्सर पूछे जाने वाले प्रश्न
अनंत लूप बाहरी कारकों पर निर्भर नहीं करता और अन्य थ्रेड्स के साथ बातचीत किए बिना एक ही ऑपरेशन दोहराता है। Livelock हमेशा अन्य थ्रेड्स की क्रियाओं पर प्रतिक्रिया है: एक थ्रेड पड़ोसी थ्रेड्स की स्थिति के जवाब में अपना व्यवहार बदलता है, एक बंद प्रतिक्रिया लूप बनाता है। Livelock के मामले में Thread Dump लगातार संदर्भ स्विचिंग दिखाता है।
डेटाबेस में, Livelock तब उत्पन्न होता है जब लेन-देन लगातार स्थगित होता है अन्य लेन-देन द्वारा लॉक के कारण। उदाहरण के लिए, DBMS wait-die एल्गोरिदम का उपयोग करता है: यदि कम प्रारंभ समय वाला लेन-देन एक नए के साथ संघर्ष करता है, तो यह रोलबैक और पुनरारंभ होता है, लेकिन हर बार उसी संघर्ष में पड़ता है। यादृच्छिक पुनरारंभ विलंब से हल किया जाता है।
कुछ सिस्टम में, Livelock Deadlock से बेहतर है क्योंकि थ्रेड सक्रिय रहते हैं और समस्या का पता लगा सकते हैं। उदाहरण के लिए, आशावादी लॉकिंग (optimistic locking) एल्गोरिदम में, livelock जैसा व्यवहार स्वीकार्य है यदि retry limit अंतिम समाप्ति की गारंटी देता है। यह प्रदर्शन और प्रगति गारंटी के बीच एक समझौता है।
Livelock को परीक्षणों में दोहराना बेहद कठिन है क्योंकि इसके लिए थ्रेड्स के सटीक समय मिलान की आवश्यकता होती है। यूनिट परीक्षण नियतात्मक रूप से चलते हैं और शायद ही कभी सक्रिय लॉक का पता लगाते हैं। लोड के तहत बार-बार चलाने और प्रोफाइलर में CPU खपत की निगरानी के साथ स्ट्रेस टेस्टिंग का उपयोग करने की सिफारिश की जाती है।
सर्वर पर, Livelock प्रदर्शन में गिरावट और टाइमआउट की ओर ले जाता है, लेकिन सर्वर क्षैतिज रूप से स्केल करता है। Android पर, Livelock बैटरी खत्म करता है और डिवाइस को गर्म करता है, जिससे सबसे खराब UX बनता है। इसके अलावा, मोबाइल डिवाइसों में सीमित संख्या में CPU कोर होते हैं, इसलिए Livelock तेजी से पूरे सिस्टम की निष्क्रियता की ओर ले जाता है।
सारांश
हम एक मोबाइल एप्लिकेशन टर्नकी विकसित करेंगे
IT Sectr 2017 से स्टार्टअप और व्यवसायों के लिए iOS और Android एप्लिकेशन बनाता है। हम आपको सलाह देंगे और सर्वोत्तम समाधान प्रस्तावित करेंगे।
यह भी पढ़ें