Deadlock একটি অবস্থা যেখানে একাধিক থ্রেড একে অপরের উপর নির্ভরশীল হয়ে থাকে এবং কোন থ্রেড তার কাজ সম্পন্ন করতে পারে না। এটি একটি গুরুতর সমস্যা, কারণ এটি প্রোগ্রামের পারফরম্যান্স পুরোপুরি থামিয়ে দিতে পারে।
Deadlock এর কারণ
Deadlock সাধারণত চারটি শর্ত পূরণ হলে ঘটে:
- Mutual Exclusion (পরস্পর বর্জন): একটি রিসোর্স একবারে শুধুমাত্র একটি থ্রেড ব্যবহার করতে পারে।
- Hold and Wait (ধরা এবং অপেক্ষা): থ্রেড একটি রিসোর্স ধরে রাখে এবং অন্য রিসোর্সের জন্য অপেক্ষা করে।
- No Preemption (কোন ছিনিয়ে নেওয়া নয়): থ্রেড তার রিসোর্স ছেড়ে না দেওয়া পর্যন্ত সেটি জোরপূর্বক নেওয়া যাবে না।
- Circular Wait (চক্রাকার অপেক্ষা): একাধিক থ্রেড একটি চক্রে রিসোর্সের জন্য একে অপরের উপর নির্ভর করে।
Deadlock Avoidance এবং Prevention টেকনিক
১. Deadlock Avoidance (ডেডলক এড়ানো)
Deadlock এড়াতে আমরা বিভিন্ন নীতিমালা ব্যবহার করতে পারি।
(i) Lock Ordering
সব থ্রেড রিসোর্সগুলো একটি নির্দিষ্ট ক্রমে (order) গ্রহণ করবে।
- উদাহরণ:
class Resource {
private final String name;
public Resource(String name) {
this.name = name;
}
public String getName() {
return name;
}
public synchronized void use(Resource other) {
System.out.println(Thread.currentThread().getName() + " is using " + this.name);
synchronized (other) {
System.out.println(Thread.currentThread().getName() + " is using " + other.name);
}
}
}
public class DeadlockAvoidanceExample {
public static void main(String[] args) {
Resource resource1 = new Resource("Resource1");
Resource resource2 = new Resource("Resource2");
Thread thread1 = new Thread(() -> resource1.use(resource2), "Thread1");
Thread thread2 = new Thread(() -> resource2.use(resource1), "Thread2");
thread1.start();
thread2.start();
}
}
কৌশল: সব থ্রেড resource1 এর পরে resource2 ব্যবহার করবে, ফলে Deadlock হবে না।
(ii) Try-Lock
ReentrantLock ক্লাসের tryLock পদ্ধতি ব্যবহার করে থ্রেড নির্দিষ্ট সময় পর্যন্ত লকের জন্য অপেক্ষা করতে পারে। সময় শেষ হলে এটি Deadlock এড়াতে পারে।
- উদাহরণ:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TryLockExample {
private static final Lock lock1 = new ReentrantLock();
private static final Lock lock2 = new ReentrantLock();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
try {
if (lock1.tryLock()) {
System.out.println("Thread1 acquired Lock1");
Thread.sleep(50);
if (lock2.tryLock()) {
System.out.println("Thread1 acquired Lock2");
} else {
System.out.println("Thread1 could not acquire Lock2");
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock1.unlock();
lock2.unlock();
}
});
Thread thread2 = new Thread(() -> {
try {
if (lock2.tryLock()) {
System.out.println("Thread2 acquired Lock2");
Thread.sleep(50);
if (lock1.tryLock()) {
System.out.println("Thread2 acquired Lock1");
} else {
System.out.println("Thread2 could not acquire Lock1");
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock2.unlock();
lock1.unlock();
}
});
thread1.start();
thread2.start();
}
}
২. Deadlock Prevention (ডেডলক প্রতিরোধ)
(i) Avoid Circular Wait
থ্রেডগুলোর মধ্যে Circular Wait এর শর্ত ভাঙুন। এর জন্য, থ্রেডগুলো একটি নির্দিষ্ট ক্রমে রিসোর্স নেবে।
- কোড:
public class CircularWaitPrevention {
private static final Object resource1 = new Object();
private static final Object resource2 = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
synchronized (resource1) {
System.out.println("Thread1 locked Resource1");
synchronized (resource2) {
System.out.println("Thread1 locked Resource2");
}
}
});
Thread thread2 = new Thread(() -> {
synchronized (resource1) { // Thread2 also locks in the same order
System.out.println("Thread2 locked Resource1");
synchronized (resource2) {
System.out.println("Thread2 locked Resource2");
}
}
});
thread1.start();
thread2.start();
}
}
(ii) Use Timeout
লকের জন্য একটি সময়সীমা নির্ধারণ করুন।
- কোড:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.TimeUnit;
public class LockTimeoutExample {
private static final Lock lock1 = new ReentrantLock();
private static final Lock lock2 = new ReentrantLock();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
try {
if (lock1.tryLock(100, TimeUnit.MILLISECONDS)) {
System.out.println("Thread1 acquired Lock1");
if (lock2.tryLock(100, TimeUnit.MILLISECONDS)) {
System.out.println("Thread1 acquired Lock2");
} else {
System.out.println("Thread1 could not acquire Lock2");
}
} else {
System.out.println("Thread1 could not acquire Lock1");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock1.unlock();
lock2.unlock();
}
});
Thread thread2 = new Thread(() -> {
try {
if (lock2.tryLock(100, TimeUnit.MILLISECONDS)) {
System.out.println("Thread2 acquired Lock2");
if (lock1.tryLock(100, TimeUnit.MILLISECONDS)) {
System.out.println("Thread2 acquired Lock1");
} else {
System.out.println("Thread2 could not acquire Lock1");
}
} else {
System.out.println("Thread2 could not acquire Lock2");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock2.unlock();
lock1.unlock();
}
});
thread1.start();
thread2.start();
}
}
৩. Monitor and Detect Deadlock
ডেডলক শনাক্ত করার জন্য ThreadMXBean ব্যবহার করা যায়।
- কোড:
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
public class DeadlockDetection {
public static void main(String[] args) {
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
long[] threadIds = threadMXBean.findDeadlockedThreads();
if (threadIds != null) {
System.out.println("Deadlock detected!");
} else {
System.out.println("No Deadlock.");
}
}
}
Deadlock প্রতিরোধ এবং এড়ানোর কৌশলগুলির মধ্যে কয়েকটি:
- Lock Ordering: একটি নির্দিষ্ট ক্রমে লক নিন।
- Timeout ব্যবহার করুন:
tryLockএর মাধ্যমে। - Avoid Circular Wait: থ্রেড চক্রাকার রিসোর্স নির্ভরতা ভাঙুন।
- Deadlock Monitor করুন:
ThreadMXBeanদিয়ে।
এই কৌশলগুলো সঠিকভাবে ব্যবহার করলে Deadlock সমস্যার সম্ভাবনা কমিয়ে আনা সম্ভব।
Read more