Livelock در توسعه موبایل: چیست، تفاوت با قفل متقابل و اصل کار

نویسنده: IT Sectr منتشر شده: 2026-03-18 زمان مطالعه: 10 دقیقه

Livelock (قفل فعال) — وضعیتی در برنامه‌نویسی چندنخی است که نخ‌ها قفل نشده‌اند، اما بی‌نهایت به اقدامات یکدیگر واکنش نشان می‌دهند بدون اینکه کار مفیدی انجام دهند. به گفته Baeldung (Java Concurrency Guide, 2024)، در Livelock نخ‌ها دائماً در پاسخ به وضعیت نخ‌های مجاور تغییر وضعیت می‌دهند، اما هیچ‌کدام به هدف نمی‌رسند. برخلاف Deadlock، Livelock 100% CPU مصرف می‌کند که باتری دستگاه موبایل را به سرعت تخلیه می‌کند.

نکات اصلی

  • Livelock — وضعیتی که نخ‌ها فعال هستند اما پیشرفتی ندارند و بی‌نهایت به تضادها واکنش نشان می‌دهند
  • برخلاف Deadlock، در Livelock نخ‌ها قفل نشده‌اند — آنها دائماً بین وضعیت‌ها جابه‌جا می‌شوند
  • قفل فعال زمان پردازنده و انرژی مصرف می‌کند و عملکرد برنامه را کاهش می‌دهد
  • شمارنده تلاش مجدد (retry limit) — ساده‌ترین راه جلوگیری از Livelock بی‌نهایت
  • تأخیر تصادفی (exponential backoff) چرخه‌های همزمان واکنش بین نخ‌ها را از بین می‌برد

Livelock چیست؟

Livelock (قفل فعال) — وضعیتی در سیستم چندنخی است که نخ‌ها قفل نشده‌اند اما کار مفیدی هم انجام نمی‌دهند. هر نخ تشخیص می‌دهد که نمی‌تواند کار را ادامه دهد و سعی می‌کند آن را اصلاح کند، اما اقدامات او همان واکنش را در سایر نخ‌ها ایجاد می‌کند. در نتیجه سیستم بی‌نهایت بین وضعیت‌ها جابه‌جا می‌شود بدون اینکه پیشرفتی حاصل شود.

قیاس کلاسیک Livelock — دو نفر در راهروی باریک با هم روبرو می‌شوند. هرکدام سعی می‌کند راه را باز کند و به کناری می‌رود، اما هر دو همزمان همان حرکت را انجام می‌دهند و دوباره روبروی هم قرار می‌گیرند. آنها ایستاده نیستند (این Deadlock بود)، بلکه فعالانه حرکت می‌کنند اما هنوز نمی‌توانند از هم عبور کنند. در برنامه‌نویسی، این معادل نخ‌هایی است که دائماً منابع را آزاد کرده و دوباره تصاحب می‌کنند.

در توسعه موبایل، Livelock به‌ویژه خطرناک است زیرا برای کاربر نامرئی است: برنامه هنگ نمی‌کند، رابط کاربری مسدود نمی‌شود، اما باتری به دلیل 100% بار CPU توسط نخ‌های پس‌زمینه 2-3 برابر سریع‌تر تخلیه می‌شود. بر اساس آزمایش‌های Google (Android Battery Optimization, 2023)، Livelock در سرویس پس‌زمینه می‌تواند عمر باتری دستگاه را 40% کاهش دهد.

Livelock چگونه ایجاد می‌شود

واکنش همزمان به تضاد

Livelock زمانی ایجاد می‌شود که چند نخ از استراتژی یکسان واکنش به تضاد استفاده می‌کنند. اگر نخ A نتواند منبعی را تصاحب کند و منبع فعلی خود را آزاد کند، و نخ B همزمان همین کار را انجام دهد، هر دو چرخه را تکرار می‌کنند — و وضعیت بی‌نهایت تکرار می‌شود. این به‌ویژه برای الگوریتم‌های TryLock و آزادسازی خودکار در صورت شکست مشخص است.

عدم تصادفی بودن در تلاش‌های مجدد

وقتی نخ‌ها از تأخیر ثابت قبل از تلاش مجدد استفاده می‌کنند، ممکن است وارد چرخه همزمان شوند. اگر هر دو نخ مدت زمان یکسانی صبر کنند، دوباره همزمان سعی در تصاحب منبع می‌کنند و همزمان آن را آزاد می‌کنند. مشکل با استفاده از exponential backoff با مؤلفه تصادفی (jitter) حل می‌شود، مانند الگوریتم CSMA/CD در Ethernet.

طراحی نادرست صف‌ها

در توسعه موبایل، Livelock اغلب در پیاده‌سازی نادرست صف‌های وظایف ایجاد می‌شود. مثلاً وقتی یک نخ کارگر پردازش پیام را تمام می‌کند، اما به دلیل منطق اولویت‌بندی دائماً کنترل را به نخ کارگر دیگری می‌دهد که همان کار را انجام می‌دهد. چنین موقعیت‌هایی برای ThreadPoolExecutorهای سفارشی با سیاست غیراستاندارد RejectedExecutionHandler معمول است.

مثال Livelock در کد Kotlin

وضعیتی را در نظر بگیرید که دو نخ از TryLock استفاده می‌کنند و در صورت شکست منبع را آزاد می‌کنند. قفل فعال ایجاد می‌شود زیرا هر دو نخ منطق یکسانی را اعمال کرده و تلاش‌ها را همزمان تکرار می‌کنند.

kotlin
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 میلی‌ثانیه صبر می‌کند و تکرار می‌کند — بی‌نهایت، با مصرف CPU. رفع مشکل — افزودن مؤلفه تصادفی به تأخیر (jitter) و محدود کردن تعداد تلاش‌های مجدد.

نسخه اصلاح‌شده از exponential backoff با jitter تصادفی استفاده می‌کند. پس از هر تلاش ناموفق، زمان انتظار با افزودن ضریب تصادفی افزایش می‌یابد که همزمانی بین نخ‌ها را از بین می‌برد.

kotlin
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: تفاوت‌های کلیدی

با وجود شباهت ظاهری، Livelock و Deadlock مکانیزم‌ها و پیامدهای اساساً متفاوتی دارند. در Deadlock نخ‌ها قفل شده و CPU مصرف نمی‌کنند — برنامه به سادگی هنگ می‌کند. در Livelock نخ‌ها فعال هستند، 100% CPU مصرف می‌کنند اما کار مفیدی انجام نمی‌دهند. انتخاب استراتژی رفع بستگی به تشخیص صحیح نوع قفل دارد.

پارامترDeadlockLivelock
وضعیت نخ‌هاBLOCKED / WAITINGRUNNABLE
مصرف CPUحداقلزیاد (90-100%)
مصرف باتریکمزیاد
تشخیصThread DumpCPU Profiler + تحلیل بصری
علت معمولترتیب متفاوت تصاحب قفل‌هااستراتژی یکسان واکنش به تضاد
رفعسلسله‌مراتب قفل‌هاRetry limit + exponential backoff

در توسعه موبایل، تفاوت عملی بسیار زیاد است. Deadlock منجر به ANR و راه‌اندازی مجدد برنامه می‌شود — از طریق Google Play Console تشخیص داده و گزارش می‌شود. Livelock نادیده گرفته می‌شود: برنامه به نظر کار می‌کند، اما باتری در یک ساعت تخلیه می‌شود و کاربر به سادگی برنامه را حذف می‌کند. طبق Firebase Analytics (App Retention Report, 2024)، 68% کاربران برنامه را حذف می‌کنند اگر در پس‌زمینه بیش از حد باتری مصرف کند.

چگونه Livelock را تشخیص دهیم

تشخیص Livelock از Deadlock دشوارتر است زیرا سیستم سیگنال‌های آشکاری نمی‌دهد — استثنا وجود ندارد، ANR وجود ندارد، پیام خطایی وجود ندارد. روش اصلی تشخیص — CPU Profiler در Android Studio. اگر نخ دائماً در وضعیت RUNNABLE باشد اما عملیات ورودی-خروجی یا محاسبات مفیدی انجام ندهد — این مشکوک به Livelock است.

نشانه اضافی — مصرف غیرعادی باتری در زمان بیکاری برنامه. Android Battery Historian (ابزاری از Android SDK) نمودارهای مصرف انرژی را بر اساس مؤلفه‌ها می‌سازد. اگر CPU Wakelock بدون دلیل ظاهری نگه داشته شود — باید Method Tracing را اجرا کرده و پشته فراخوانی نخ‌های مشکوک را تحلیل کرد.

در سطح کد، ثبت تلاش‌های مجدد با threadId و زمان کمک می‌کند. اگر لاگ هزاران تلاش مجدد در ثانیه را بدون هیچ موفقیتی نشان دهد — این Livelock است. توصیه می‌شود از circuit breaker مشابه Hystrix یا شمارنده retry با آستانه فعال‌سازی استفاده شود که پس از عبور از حد، عملیات را غیرفعال کرده و توسعه‌دهنده را از طریق Crashlytics مطلع می‌کند.

روش‌های جلوگیری از قفل فعال

شمارنده تلاش مجدد (Retry Limit)

ساده‌ترین و مطمئن‌ترین روش — محدود کردن تعداد تلاش‌ها برای تصاحب منبع. اگر پس از N تلاش عملیات موفق نشد، نخ به وضعیت خطا رفته و به کاربر اطلاع می‌دهد. N به صورت تجربی انتخاب می‌شود: برای برنامه‌های موبایل معمولاً 3-5 تلاش. این کار Livelock بی‌نهایت را به قیمت فعال‌سازی‌های نادر نادرست در بار بالا کاملاً حذف می‌کند.

Exponential Backoff با Jitter

به جای تأخیر ثابت بین تلاش‌ها از مکث نمایی افزایش‌یابنده با مؤلفه تصادفی استفاده می‌شود. فرمول: delay = min(baseDelay * 2^attempt, maxDelay) + random(0, jitter). این رویکرد نه تنها همزمانی نخ‌ها را از بین می‌برد، بلکه بار کلی سیستم را در رقابت بالا کاهش می‌دهد. در الگوریتم‌های پروتکل‌های شبکه استفاده می‌شود و توسط Google برای منطق تلاش مجدد Firebase Realtime Database توصیه شده است.

اولویت و منطق نامتقارن

اختصاص استراتژی‌های متفاوت به نخ‌های مختلف علت اصلی Livelock — واکنش یکسان به تضاد — را از بین می‌برد. مثلاً نخ با اولویت بالا منبع را بدون آزادسازی دریافت می‌کند و نخ با اولویت پایین آزاد کرده و منتظر می‌ماند. در توسعه موبایل، نخ UI می‌تواند در تصاحب قفل‌ها اولویت داشته باشد و نخ‌های کارگر پس‌زمینه از TryLock با تایم‌اوت استفاده کنند.

عدم آزادسازی چرخه‌ای

در برخی معماری‌ها، Livelock در سطح طراحی جلوگیری می‌شود: آزادسازی منابع فقط در یک جهت. مثلاً اگر نخ A همیشه کنترل را از طریق یک کانال ثابت (Channel) به نخ B منتقل کند و B هرگز سعی در بازگرداندن کنترل به A نداشته باشد — چرخه واکنش غیرممکن است. معماری خط لوله با مراحل پردازش یک‌طرفه در Android CameraX و MediaPipe کاملاً Livelock را بین مراحل مجاور حذف می‌کند.

سوالات متداول

چگونه Livelock را از حلقه بی‌نهایت تشخیص دهیم؟

حلقه بی‌نهایت به عوامل خارجی وابسته نیست و یک عملیات را بدون تعامل با سایر نخ‌ها تکرار می‌کند. Livelock همیشه واکنشی به اقدامات سایر نخ‌ها است: نخ رفتار خود را در پاسخ به وضعیت نخ‌های مجاور تغییر می‌دهد و یک بازخورد بسته ایجاد می‌کند. Thread Dump در Livelock جابه‌جایی دائمی زمینه را نشان می‌دهد.

Livelock در زمینه پایگاه داده چیست؟

در پایگاه داده، Livelock زمانی ایجاد می‌شود که تراکنش دائماً به تعویق می‌افتد به دلیل قفل‌های تراکنش‌های دیگر. مثلاً DBMS از الگوریتم wait-die استفاده می‌کند: اگر تراکنش با زمان شروع کمتر با تراکنش جدیدتر تضاد داشته باشد، برگردانده و دوباره راه‌اندازی می‌شود، اما هر بار به همان تضاد برخورد می‌کند. با randomized restart delay حل می‌شود.

چه زمانی Livelock مفید است؟

در برخی سیستم‌ها Livelock از Deadlock ترجیح‌دادنی‌تر است زیرا نخ‌ها فعال می‌مانند و می‌توانند مشکل را تشخیص دهند. مثلاً در الگوریتم‌های قفل خوش‌بینانه (optimistic locking) رفتار مشابه Livelock در صورت تضمین retry limit مجاز است. این مصالحه‌ای بین عملکرد و تضمین پیشرفت است.

Livelock چگونه بر آزمایش تأثیر می‌گذارد؟

Livelock در آزمایش‌ها بسیار دشوار بازتولید می‌شود زیرا نیاز به تطابق دقیق زمان‌بندی نخ‌ها دارد. تست‌های واحد به صورت قطعی اجرا می‌شوند و به ندرت قفل فعال را تشخیص می‌دهند. توصیه می‌شود از Stress Testing با اجرای مکرر تحت بار و نظارت بر مصرف CPU در پروفایلر استفاده شود.

تفاوت Livelock در Android با Livelock در سرور چیست؟

در سرور، Livelock منجر به کاهش عملکرد و timeout می‌شود، اما سرور به صورت افقی مقیاس‌پذیر است. در Android، Livelock باتری را تخلیه کرده و دستگاه را داغ می‌کند و تجربه کاربری بدتری ایجاد می‌کند. علاوه بر این، در دستگاه‌های موبایل تعداد هسته‌های CPU محدود است، بنابراین Livelock سریع‌تر به عدم کارایی کل سیستم منجر می‌شود.

خلاصه

  • Livelock — وضعیت قفل فعال که نخ‌ها قفل نشده‌اند اما بدون پیشرفت بی‌نهایت به تضادها واکنش نشان می‌دهند
  • برخلاف Deadlock، در Livelock نخ‌ها 100% CPU مصرف می‌کنند که برای دستگاه‌های موبایل حیاتی است
  • علت اصلی — استراتژی یکسان واکنش به تضاد و عدم تصادفی بودن در تأخیرها
  • Exponential backoff با jitter چرخه‌های همزمان را می‌شکند و از قفل فعال جلوگیری می‌کند
  • Retry limit (3-5 تلاش) Livelock بی‌نهایت را کاملاً حذف می‌کند
  • CPU Profiler در Android Studio و Battery Historian — ابزارهای اصلی تشخیص Livelock
  • منطق نامتقارن تصاحب منابع برای نخ‌های مختلف امکان قفل فعال را از بین می‌برد

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید