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 + দৃশ্য বিশ্লেষণ |
| সাধারণ কারণ | লক অর্জনের ভিন্ন ক্রম | দ্বন্দ্বে একই প্রতিক্রিয়া কৌশল |
| সমাধান | লক শ্রেণিবিন্যাস | 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 অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।
আরও পড়ুন