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 پالیسیوں والے کسٹم ThreadPoolExecutor کے لیے مخصوص ہے۔
ایسی صورت حال پر غور کریں جہاں دو تھریڈ 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 + بصری تجزیہ |
| عام وجہ | لاک حاصل کرنے کا مختلف ترتیب | تنازع پر ایک جیسی ردعمل کی حکمت عملی |
| اصلاح | لاک کا درجہ بندی | دوبارہ کوشش کی حد + 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 جیسا سرکٹ بریکر یا ایک حد کے ساتھ 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 جیسا رویہ قابل قبول ہے اگر دوبارہ کوشش کی حد حتمی تکمیل کی ضمانت دے۔ یہ کارکردگی اور پیش رفت کی ضمانت کے درمیان ایک سمجھوتہ ہے۔
Livelock ٹیسٹوں میں دوبارہ پیدا کرنا انتہائی مشکل ہے کیونکہ اس کے لیے تھریڈز کے عین وقت کے ملاپ کی ضرورت ہوتی ہے۔ یونٹ ٹیسٹ تعیینی طور پر چلتے ہیں اور شاذ و نادر ہی فعال لاک کا پتہ لگاتے ہیں۔ بوجھ کے تحت بار بار چلانے اور پروفائلر میں CPU استعمال کی نگرانی کے ساتھ اسٹریس ٹیسٹنگ استعمال کرنے کی سفارش کی جاتی ہے۔
سرور پر، Livelock کارکردگی میں کمی اور ٹائم آؤٹ کا سبب بنتا ہے، لیکن سرور افقی طور پر پیمانہ کرتا ہے۔ Android پر، Livelock بیٹری ختم کرتا ہے اور ڈیوائس کو گرم کرتا ہے، بدترین صارف تجربہ پیدا کرتا ہے۔ اس کے علاوہ، موبائل ڈیوائسز میں CPU کور کی محدود تعداد ہوتی ہے، اس لیے Livelock تیزی سے پورے نظام کے ناکارہ ہونے کا باعث بنتا ہے۔
خلاصہ
ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے
IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔
مزید پڑھیں