Java তে Multithreading এবং Concurrency দুটি গুরুত্বপূর্ণ কনসেপ্ট যা একসাথে একাধিক কাজ সম্পাদন করতে সহায়তা করে। এগুলি পারফর্মেন্স উন্নত করার জন্য বিশেষভাবে ব্যবহার করা হয়, যেখানে একাধিক থ্রেড একে অপরের সাথে একই সময়ে কাজ করে। তবে, এই থ্রেডগুলির মধ্যে সঠিক সমন্বয় (Synchronization) প্রয়োজন যাতে কোনো সমস্যা (Race Condition) সৃষ্টি না হয়।
১. Multithreading কি?
উত্তর:
Multithreading হল একটি প্রোগ্রামিং কৌশল যার মাধ্যমে একক প্রোগ্রামে একাধিক থ্রেডের মাধ্যমে বিভিন্ন কাজ একযোগে সম্পাদিত হয়। Java তে, থ্রেড হল একটি প্রক্রিয়া যা একটি নির্দিষ্ট কাজ সম্পাদন করে এবং Java-তে একাধিক থ্রেড ব্যবহার করে একাধিক কাজ একসাথে করা যায়।
- Thread: এটি একটি স্বতন্ত্র কার্যক্রম যা একসাথে একাধিক কাজ করার জন্য ব্যবহৃত হয়।
- Java তে,
Threadক্লাস এবংRunnableইন্টারফেসের মাধ্যমে Multithreading প্রয়োগ করা হয়।
২. Concurrency কি?
উত্তর:
Concurrency হল এমন একটি প্রোগ্রামিং কৌশল যা একাধিক কাজের মধ্যে সমন্বয়ের মাধ্যমে একযোগে কাজ করার জন্য ব্যবহৃত হয়। এটি নিশ্চিত করে যে একাধিক কাজ একই সময়ে কার্যকরভাবে সম্পাদিত হচ্ছে এবং কোনো ধরণের লক বা ব্লকিং সমস্যা ছাড়াই একাধিক থ্রেড চলতে পারে।
- Concurrency থ্রেডগুলির মধ্যে সমন্বয় নিশ্চিত করতে এবং একাধিক কাজ সঠিকভাবে সমন্বিতভাবে সম্পন্ন করার জন্য ব্যবহৃত হয়।
- Multithreading একটি উপাদান যা Concurrency অর্জনের জন্য ব্যবহৃত হতে পারে।
৩. Java তে Multithreading তৈরি করার পদ্ধতি কী?
Java তে Multithreading তৈরি করার দুটি প্রধান পদ্ধতি রয়েছে:
- Thread ক্লাসের সাবক্লাস তৈরি করা
- Runnable ইন্টারফেস ইমপ্লিমেন্ট করা
১. Thread ক্লাসের সাবক্লাস তৈরি করা
Java তে Thread ক্লাসকে অবরোহণ (inherit) করে থ্রেড তৈরি করা যায় এবং তারপরে run() মেথডটি ওভাররাইড করে থ্রেডের কাজটি নির্ধারণ করা হয়।
class MyThread extends Thread {
public void run() {
System.out.println("Thread is running");
}
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start(); // start() মেথড দিয়ে থ্রেড শুরু করা হয়
}
}
২. Runnable ইন্টারফেস ইমপ্লিমেন্ট করা
Runnable ইন্টারফেস ইমপ্লিমেন্ট করে, run() মেথডটি প্রদান করতে হয় এবং তারপর Thread ক্লাসের একটি অবজেক্ট তৈরি করে start() মেথড দিয়ে থ্রেড শুরু করা হয়।
class MyRunnable implements Runnable {
public void run() {
System.out.println("Runnable thread is running");
}
public static void main(String[] args) {
MyRunnable r = new MyRunnable();
Thread t1 = new Thread(r);
t1.start(); // start() মেথড দিয়ে থ্রেড শুরু করা হয়
}
}
৪. Thread Life Cycle কি?
উত্তর:
একটি থ্রেডের জীবনকাল বিভিন্ন স্টেট থেকে চলে:
- New (রচনা করা): যখন একটি থ্রেড তৈরি করা হয়, কিন্তু এখনও চলেনি।
- Runnable (চলছে): থ্রেডের কাজ শুরু হয়েছে, তবে এটি CPU থেকে রান করার জন্য অপেক্ষা করছে।
- Blocked (ব্লকড): থ্রেড একটি রিসোর্সের জন্য অপেক্ষা করছে (যেমন, I/O বা সিঙ্ক্রোনাইজড ব্লক)।
- Waiting: থ্রেড অন্য থ্রেডের সম্পূর্ণ হওয়ার জন্য অপেক্ষা করছে।
- Terminated (শেষ): থ্রেড তার কাজ শেষ করেছে এবং বন্ধ হয়ে গেছে।
৫. Thread Synchronization কি এবং কেন এটি প্রয়োজন?
উত্তর:
Synchronization হল এমন একটি কৌশল যা একাধিক থ্রেডকে একই সময়ে একটি রিসোর্স অ্যাক্সেস করতে বাধা দেয়। এটি নিশ্চিত করে যে একাধিক থ্রেড যখন একই রিসোর্সে অ্যাক্সেস করতে যায়, তখন কোন ভুল বা Race Condition না ঘটে।
Race Condition হল একটি পরিস্থিতি যেখানে একাধিক থ্রেড একে অপরের সাথে প্রতিযোগিতা করে একই রিসোর্সে অ্যাক্সেস করতে, যার ফলে ফলস্বরূপ ভুল বা অস্পষ্ট আচরণ ঘটে।
Synchronization উদাহরণ:
class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
public class SyncExample {
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Final count: " + counter.getCount());
}
}
এখানে synchronized কিওয়ার্ড ব্যবহার করা হয়েছে যাতে এক সময় একটিই থ্রেড increment() মেথডটি ব্যবহার করতে পারে এবং রেস কন্ডিশন সৃষ্টি না হয়।
৬. Deadlock কি?
উত্তর:
Deadlock হল একটি পরিস্থিতি যেখানে দুটি বা তার বেশি থ্রেড একে অপরকে প্রয়োজনীয় রিসোর্সের জন্য অপেক্ষা করে, কিন্তু কেউই তাদের কাজ শেষ করতে পারে না। এর ফলে, প্রোগ্রামটি চলতে থাকে, কিন্তু কোন থ্রেডই সম্পন্ন হয় না।
Deadlock এড়ানোর জন্য কৌশল:
- Lock Ordering: সমস্ত থ্রেডের জন্য রিসোর্সের একটি নির্দিষ্ট অর্ডার সেট করা।
- Timeout: যদি থ্রেডটি কিছু সময়ের মধ্যে রিসোর্স না পায়, তাহলে থ্রেডটি রোলব্যাক করে এবং পুনরায় চেষ্টা করে।
৭. Java তে Executor Framework কি?
উত্তর:
Executor Framework হল Java এর একটি উচ্চ স্তরের API যা থ্রেড ব্যবস্থাপনা সহজ করে। এটি থ্রেড পুলের মাধ্যমে থ্রেড তৈরি এবং চালানোর কাজ করে, যাতে থ্রেডগুলি পুনঃব্যবহারযোগ্য হয় এবং থ্রেড সৃষ্টির জন্য অতিরিক্ত খরচ কমে।
- ExecutorService ইন্টারফেসটি ব্যবহৃত হয়, যা থ্রেড পুল পরিচালনা করতে সহায়তা করে।
Executors.newFixedThreadPool()পদ্ধতি ব্যবহার করে নির্দিষ্ট সংখ্যক থ্রেডের পুল তৈরি করা যায়।
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ExecutorExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(2);
Runnable task1 = () -> {
System.out.println("Task 1 is running");
};
Runnable task2 = () -> {
System.out.println("Task 2 is running");
};
executor.execute(task1);
executor.execute(task2);
executor.shutdown();
}
}
৮. Java তে Concurrency এবং Multithreading এর মধ্যে পার্থক্য কি?
উত্তর:
- Concurrency হল একাধিক কাজের সমন্বয়, যেখানে একাধিক কাজ একই সময়ে সম্পাদিত হলেও একে অপরের সাথে সুসংগঠিতভাবে কাজ করতে পারে।
- Multithreading হল একাধিক থ্রেডের মাধ্যমে একাধিক কাজ চালানো।
Java তে Multithreading এবং Concurrency ব্যবহৃত হয় অ্যাপ্লিকেশনের পারফরম্যান্স এবং কার্যকারিতা বাড়াতে। Synchronization এবং Deadlock এড়ানোর কৌশলগুলি জানা জরুরি, কারণ এগুলি অ্যাপ্লিকেশন চলাকালীন সমস্যা সৃষ্টি করতে পারে। Executor Framework ব্যবহারের মাধ্যমে থ্রেড ব্যবস্থাপনাও সহজতর হয়।
Multithreading হল একটি প্রোগ্রামিং কৌশল যা একাধিক থ্রেড ব্যবহার করে একসাথে একাধিক কাজ সম্পাদন করতে সহায়তা করে। এটি একটি একক প্রক্রিয়াতে একাধিক কার্যকরী অংশকে চালানোর মাধ্যমে আরও কার্যকরী এবং দ্রুত অ্যাপ্লিকেশন তৈরি করার একটি পদ্ধতি।
Multithreading এর সংজ্ঞা:
থ্রেড হলো একটি সাব-প্রসেস যা একটি প্রক্রিয়ায় কাজ করে। একাধিক থ্রেড একসাথে চলতে পারে, যার ফলে একাধিক কাজ একসাথে সম্পন্ন করা যায়। একটি প্রক্রিয়া বিভিন্ন থ্রেড নিয়ে গঠিত হতে পারে এবং প্রতিটি থ্রেড নির্দিষ্ট একটি কাজ সম্পাদন করে।
Multithreading এর মাধ্যমে একাধিক থ্রেড একসাথে চলতে পারে, কিন্তু এগুলি একে অপরের সাথে শেয়ারড রিসোর্স (যেমন মেমরি) ব্যবহার করে কাজ করে।
Multithreading এর প্রয়োজনীয়তা:
- বহুমুখী কাজ একসাথে সম্পাদন (Parallel Execution):
- Multithreading ব্যবহারের মাধ্যমে, একাধিক কাজ একই সময়ে একসাথে চালানো সম্ভব হয়। এটি কার্যকারিতা বৃদ্ধি করে এবং সম্পাদনার সময় কমায়। যেমন, আপনি একদিকে ফাইল ডাউনলোড করতে পারেন এবং অন্যদিকে ডেটা প্রসেস করতে পারেন।
- সার্বজনীন প্রতিক্রিয়া (Improved Application Responsiveness):
- Multithreading অ্যাপ্লিকেশনকে আরও প্রতিক্রিয়া-সক্ষম করে তোলে। একাধিক থ্রেডের মাধ্যমে, অ্যাপ্লিকেশন ইউজার ইন্টারফেস (UI) থ্রেডকে ব্লক না করে অন্য কাজ করতে পারে, যা ব্যবহারকারীর অভিজ্ঞতাকে আরও উন্নত করে।
- রিসোর্স অপটিমাইজেশন (Resource Optimization):
- যদি একাধিক থ্রেড একে অপরের সাথে কাজ করে, তাহলে CPU এর শক্তি আরও ভালভাবে ব্যবহৃত হয়। একটি থ্রেড যদি কোনো I/O অপারেশন যেমন ফাইল রিডিং বা নেটওয়ার্ক কলের জন্য অপেক্ষা করে, অন্য থ্রেড তখন CPU এর সম্পদ ব্যবহার করতে পারে, যা সম্পদের অপচয় কমায়।
- ডাটাবেস, ফাইল, নেটওয়ার্ক প্রক্রিয়াগুলির জন্য কার্যকরী (Efficient for I/O Operations):
- Multithreading I/O অপারেশন যেমন ডাটাবেস অ্যাক্সেস, নেটওয়ার্ক কল, বা ফাইল রিডিং/রাইটিং খুব দ্রুত করতে সহায়তা করে, কারণ একাধিক থ্রেড একই সময়ে এসব অপারেশন করতে পারে।
- এন্টারপ্রাইজ অ্যাপ্লিকেশনের জন্য উপযোগী (Suitable for Enterprise Applications):
- বড় আকারের এন্টারপ্রাইজ অ্যাপ্লিকেশনগুলিতে যেখানে বিভিন্ন সার্ভিস একসাথে চলতে থাকে, যেমন ব্যাংকিং বা ট্রেডিং সিস্টেম, সেখানে multithreading অ্যাপ্লিকেশনকে অনেক কার্যকরী ও দ্রুত করে তোলে।
Multithreading এর উপকারিতা:
- ভাল পারফরম্যান্স (Better Performance):
- একাধিক থ্রেড একসাথে কাজ করে পারফরম্যান্স বৃদ্ধি পায়, বিশেষ করে যদি একাধিক কোর সহ মাল্টি-কোর প্রসেসর থাকে।
- ইউজার ইন্টারফেসে স্নিগ্ধতা (Smooth User Interface):
- ইউজার ইন্টারফেসে থ্রেডিং ব্যবহার করা হলে ইউজার ইন্টারফেসটি ব্লক না হয়ে চালু থাকে, ফলে অ্যাপ্লিকেশনটি আরো স্মুথ এবং প্রতিক্রিয়া-সক্ষম হয়ে ওঠে।
- কম সময় এবং কম খরচে কাজ (Reduced Time and Cost):
- একই সময়ে একাধিক কাজ সম্পাদন করার কারণে, সময় এবং খরচ কম হয়।
- ভালো রিসোর্স ম্যানেজমেন্ট (Better Resource Management):
- বিভিন্ন থ্রেড CPU, মেমরি, এবং অন্যান্য রিসোর্স সুষ্ঠুভাবে ভাগ করে ব্যবহার করে।
Multithreading এর উদাহরণ:
ধরা যাক, আপনি একটি অ্যাপ্লিকেশন তৈরি করছেন যেখানে ডেটা প্রসেসিং এবং ফাইল ডাউনলোড একসাথে চলতে হবে। একে একসাথে চালানোর জন্য Multithreading ব্যবহৃত হবে।
class DownloadThread extends Thread {
public void run() {
// ফাইল ডাউনলোড করার কোড
System.out.println("File downloading...");
}
}
class ProcessDataThread extends Thread {
public void run() {
// ডেটা প্রসেস করার কোড
System.out.println("Processing data...");
}
}
public class MultithreadingExample {
public static void main(String[] args) {
DownloadThread downloadThread = new DownloadThread();
ProcessDataThread processDataThread = new ProcessDataThread();
downloadThread.start(); // ফাইল ডাউনলোড শুরু
processDataThread.start(); // ডেটা প্রসেস শুরু
}
}
এখানে, DownloadThread এবং ProcessDataThread একসাথে চলতে পারে, ফলে ফাইল ডাউনলোড হওয়ার পাশাপাশি ডেটা প্রসেসিংও হতে থাকবে। এটি পারফরম্যান্স এবং ইউজার অভিজ্ঞতা উন্নত করে।
Multithreading এর চ্যালেঞ্জসমূহ:
- Thread Safety:
- একাধিক থ্রেড একই ডাটা শেয়ার করলে সমস্যা হতে পারে, যেমন race conditions বা data corruption। সুতরাং, সঠিকভাবে synchronization এবং thread safety নিশ্চিত করা জরুরি।
- Deadlock:
- যখন একাধিক থ্রেড একে অপরের উপর নির্ভরশীল হয়ে আটকে যায় এবং তাদের কাজ এগোতে থাকে না, তখন সেটি Deadlock হতে পারে। এই পরিস্থিতি এড়াতে সঠিকভাবে লক ব্যবস্থাপনা করা প্রয়োজন।
- Context Switching:
- একাধিক থ্রেডের মধ্যে দ্রুত পরিবর্তন (context switching) হলে কিছু সময় অতিরিক্ত খরচ হতে পারে, কারণ CPU মেমরির অবস্থান স্যুইচ করে।
Multithreading একটি শক্তিশালী টুল যা অ্যাপ্লিকেশনগুলির কর্মক্ষমতা, ইউজার ইন্টারফেসের স্নিগ্ধতা এবং রিসোর্স ব্যবহারের দক্ষতা বৃদ্ধি করতে সহায়তা করে। তবে, সঠিকভাবে ব্যবহৃত না হলে এটি চ্যালেঞ্জ তৈরি করতে পারে, যেমন deadlocks, race conditions, এবং thread safety এর সমস্যা। তাই, Multithreading ব্যবহারের সময় সাবধানতা অবলম্বন করা প্রয়োজন।
জাভায় থ্রেড পরিচালনা করার জন্য একটি নির্দিষ্ট Thread Lifecycle বা থ্রেডের জীবনচক্র রয়েছে, যার মধ্যে একটি থ্রেড বিভিন্ন State-এ চলে যেতে পারে। নিচে Thread Lifecycle এবং Thread States সম্পর্কে বিস্তারিত আলোচনা করা হলো:
1. Thread Lifecycle (থ্রেড জীবনচক্র)
থ্রেডের জীবনচক্রে থ্রেডের বিভিন্ন অবস্থান বা state রয়েছে, যা নির্ধারণ করে থ্রেডের অবস্থা কেমন। থ্রেডের Lifecycle কে সাধারণত পাঁচটি প্রধান স্টেজে ভাগ করা হয়:
- New State (New/Not Started):
- যখন একটি থ্রেড তৈরি করা হয় কিন্তু এখনও তা চালু হয়নি, তখন এটি New অবস্থায় থাকে।
- এই অবস্থায়, থ্রেডটি প্রস্তুতি গ্রহণ করছে কিন্তু কাজ শুরু করেনি।
- Runnable State (Ready to run):
- Runnable অবস্থা হলো সেই অবস্থা যখন থ্রেডটি প্রস্তুত থাকে কাজ শুরু করার জন্য।
- থ্রেডটি যখন run() মেথড কল করা হয় তখন এটি Runnable অবস্থায় চলে যায়।
- তবে, থ্রেডটি কার্যকর হতে হতে CPU এর উপর নির্ভর করে, এবং কেবলমাত্র যখন CPU অ্যাক্সেস উপলব্ধ হবে, তখন থ্রেডটি রান করবে।
- Blocked State:
- Blocked অবস্থা হলো যখন থ্রেড একটি নির্দিষ্ট রিসোর্স (যেমন, I/O অপারেশন বা অন্য থ্রেডের জন্য লক) অ্যাক্সেস করার জন্য অপেক্ষা করছে।
- এটি যখন synchronized ব্লক বা মেথড ব্যবহার করে কোনো রিসোর্স অ্যাক্সেস করার চেষ্টা করে এবং ঐ রিসোর্স অন্য থ্রেড দ্বারা ব্যস্ত থাকে তখন ব্লক হয়।
- Waiting State:
- Waiting অবস্থা হলো যখন একটি থ্রেড অন্য থ্রেডের সিগন্যাল বা নির্দিষ্ট সময়ের জন্য অপেক্ষা করছে।
- থ্রেডটি
Object.wait(),Thread.join(), বাThread.sleep()মেথড কল করার পরে এই অবস্থায় চলে যায়।
- Terminated State (Dead):
- একটি থ্রেড Terminated অবস্থায় চলে যায় যখন তার কার্যক্রম সম্পূর্ণ হয় বা কোনো কারণে থ্রেডটি শেষ হয়ে যায়।
- থ্রেডের কাজ শেষ হলে, এটি dead অবস্থায় চলে যায় এবং এর পর কোনো কাজ করতে সক্ষম হয় না।
2. Thread States (থ্রেড স্টেটস)
জাভাতে একটি থ্রেড মোট পাঁচটি ভিন্ন state এ থাকতে পারে:
1. New (Newborn State)
- থ্রেড তৈরি হলে, এটি New state তে চলে যায়।
এই অবস্থায় থ্রেডটি run() মেথড কল করার জন্য প্রস্তুত থাকে, তবে এখনও চালু হয়নি।
উদাহরণ:
Thread t = new Thread();
2. Runnable (Ready to Run)
- থ্রেড যখন start() মেথড কল করা হয়, তখন এটি Runnable অবস্থায় চলে যায়।
Runnable অবস্থায় থাকা থ্রেডটি CPU থেকে কার্যকর হতে পারে, তবে এটি CPU থেকে অ্যাক্সেস পাওয়ার জন্য অপেক্ষা করতে পারে।
উদাহরণ:
t.start(); // Now the thread moves to the Runnable state
3. Blocked
- Blocked অবস্থায় থাকা থ্রেডগুলি অন্য থ্রেডের রিসোর্স (যেমন সিঙ্ক্রোনাইজড মেথড বা ব্লক) অ্যাক্সেসের জন্য অপেক্ষা করে।
এই অবস্থায় থ্রেড CPU তে থাকে না, বরং অন্য থ্রেড রিসোর্সের ব্যবহার শেষ হওয়ার অপেক্ষা করে।
উদাহরণ:
synchronized void method() { // method body }
4. Waiting
- Waiting অবস্থায় থাকা থ্রেডগুলি অন্য থ্রেডের ইন্টারভেনশন বা নির্দিষ্ট সময়ের জন্য অপেক্ষা করে।
এটি
Object.wait(),Thread.join(), অথবাThread.sleep()মেথড ব্যবহার করলে ঘটে।উদাহরণ:
synchronized (object) { object.wait(); // Thread goes into Waiting state }
5. Terminated (Dead)
- Terminated অবস্থা হলো থ্রেডের শেষ অবস্থা, যখন থ্রেডের কার্যক্রম সম্পূর্ণ হয় বা থ্রেডটি কোনো কারণে মারা যায়।
থ্রেড একটি কাজ শেষ করার পরে, এটি আর পুনরায় কাজ শুরু করতে পারে না।
উদাহরণ:
// After the run() method completes, the thread enters Dead state.
3. Thread Life Cycle Diagram
নিচে একটি সাধারণ থ্রেড লাইফ সাইকেল ডায়াগ্রাম দেখানো হলো, যা থ্রেডের বিভিন্ন স্টেট এবং তাদের মধ্যে সম্পর্ক নির্দেশ করে:
+-----------+
| New | ------> Thread() is created
+-----------+
|
v
+-----------+
| Runnable | ------> thread.start() is called
+-----------+
|
v
+-----------+
| Blocked | ------> Thread is blocked waiting for resources
+-----------+
|
v
+-----------+
| Waiting | ------> Thread waits for a signal or time
+-----------+
|
v
+-----------+
| Terminated| ------> Thread finishes execution
+-----------+
4. Thread States Example
এখানে একটি সিম্পল থ্রেড লাইফ সাইকেল উদাহরণ দেওয়া হলো:
class MyThread extends Thread {
public void run() {
try {
System.out.println("Thread is running");
Thread.sleep(1000); // Goes into Waiting state for 1 second
} catch (InterruptedException e) {
System.out.println(e);
}
}
public static void main(String[] args) {
MyThread t1 = new MyThread();
System.out.println("Thread state after creation: " + t1.getState());
t1.start(); // Moves to Runnable state
System.out.println("Thread state after starting: " + t1.getState());
}
}
5. Summary of Thread States
| State | Description |
|---|---|
| New | Thread is created but not started yet. |
| Runnable | Thread is ready to run, but it is waiting for CPU time. |
| Blocked | Thread is blocked waiting for a resource or lock. |
| Waiting | Thread is waiting for a specific condition or signal. |
| Terminated | Thread has completed its task or was interrupted and is no longer running. |
Java Thread Lifecycle এবং Thread States বুঝে গেলে, আপনি থ্রেড পরিচালনা করতে অনেক বেশি দক্ষ হবেন। থ্রেডের জীবনচক্রের প্রতিটি স্টেটের মধ্যে থ্রেড কিভাবে চলে এবং কিভাবে আপনি থ্রেডের আচরণ নিয়ন্ত্রণ করতে পারবেন, তা জানা আপনার জন্য গুরুত্বপূর্ণ।
Java তে Synchronized শব্দটি Multithreading প্রোগ্রামিংয়ের মধ্যে ব্যবহৃত হয়, যা একাধিক থ্রেডের মধ্যে রেস কন্ডিশন বা ডেটা inconsistency রোধ করে। এটি একটি থ্রেডকে অন্য থ্রেডের সাথে একই সময় একই রিসোর্স অ্যাক্সেস করতে বাধা দেয়। Java তে Synchronized ব্লক এবং Synchronized মেথড ব্যবহৃত হয় একই উদ্দেশ্যে, কিন্তু তাদের মধ্যে কিছু পার্থক্য রয়েছে।
1. Synchronized মেথড (Synchronized Method):
Synchronized মেথড হল একটি মেথড যেটি synchronized কিওয়ার্ড দ্বারা চিহ্নিত করা হয়। যখন একটি মেথড synchronized হয়, তখন এটি শুধুমাত্র একটি থ্রেডের দ্বারা একবারে একযোগভাবে এক্সিকিউট করা যেতে পারে। যখন একটি থ্রেড সেই মেথড কল করে, তখন এটি ঐ মেথডের ব্লকের সাথে সংশ্লিষ্ট অবজেক্ট লক অথবা ক্লাস লক নিয়ে কাজ করবে।
- এটি কীভাবে কাজ করে?
- যদি একটি ইনস্ট্যান্স মেথড
synchronizedহয়, তাহলে ঐ মেথডের কল করার সময় ঐ অবজেক্টের উপর একটি লক নেওয়া হয়। অর্থাৎ, অন্য কোন থ্রেড ঐ একই অবজেক্টেরsynchronizedমেথডে প্রবেশ করতে পারবে না যতক্ষণ না প্রথম থ্রেড মেথডটি সম্পন্ন করে। - যদি এটি একটি স্ট্যাটিক মেথড হয়, তাহলে মেথডটি ক্লাস লক নেবে এবং পুরো ক্লাসের জন্য এটি প্রযোজ্য হবে।
- যদি একটি ইনস্ট্যান্স মেথড
উদাহরণ:
class Counter { private int count = 0; // Synchronized method public synchronized void increment() { count++; } }- বিবরণ: এখানে
increment()মেথডেsynchronizedকিওয়ার্ড ব্যবহার করা হয়েছে। এর মানে হল যে, শুধুমাত্র এক থ্রেড এক সময়ে এই মেথডটি কল করতে পারবে এবং অন্য থ্রেড এটি ব্যবহার করতে পারবে না যতক্ষণ না প্রথম থ্রেড মেথডটি শেষ করে।
2. Synchronized ব্লক (Synchronized Block):
Synchronized ব্লক হল একটি নির্দিষ্ট কোড ব্লক যা synchronized কিওয়ার্ড দ্বারা ঘিরে রাখা হয়। এটি অবজেক্টের নির্দিষ্ট অংশে লক করার জন্য ব্যবহৃত হয়, যাতে শুধুমাত্র সেই নির্দিষ্ট অংশের উপরই থ্রেড এক্সিকিউশন নিয়ন্ত্রণ করা যায়। Synchronized ব্লক কোডের নির্দিষ্ট অংশে লক প্রয়োগ করে, তাই এটি আরও লিগার এবং প্রয়োজনীয় অবস্থায় লক ব্যবহার করার সুযোগ দেয়।
- এটি কীভাবে কাজ করে?
Synchronizedব্লক সাধারণত একটি নির্দিষ্ট অবজেক্টের লক নিয়ে কাজ করে এবং নির্দিষ্ট ব্লক বা কোডের অংশের জন্য এক থ্রেড লক প্রয়োগ করে। এটি তখন কাজ করবে যখন আপনি চান যে শুধু একটি নির্দিষ্ট অংশের জন্য লক প্রয়োগ করা হোক, সব মেথডের জন্য নয়।- এটি মেথডের তুলনায় আরো অর্গানাইজড এবং ফাইন টিউন করা যায়, কারণ আপনি জানিয়ে দিতে পারবেন কোন অবজেক্টের উপর লক প্রয়োগ করতে হবে।
উদাহরণ:
class Counter { private int count = 0; public void increment() { synchronized(this) { count++; } } }- বিবরণ: এখানে
increment()মেথডের মধ্যেsynchronizedব্লক ব্যবহার করা হয়েছে।thisএর মাধ্যমেCounterঅবজেক্টের উপর লক প্রয়োগ করা হয়েছে, এবং এটি শুধুমাত্রcount++কোডের অংশে কাজ করবে। অর্থাৎ, অন্যান্য কোড অংশে কোনো থ্রেড বাধা পাবে না।
Synchronized ব্লক এবং Synchronized মেথডের মধ্যে পার্থক্য:
| পদার্থ | Synchronized মেথড | Synchronized ব্লক |
|---|---|---|
| লকিং অ্যাক্সেস | পুরো মেথডটি একসাথে লক করা হয়। | কোডের নির্দিষ্ট অংশ একসাথে লক করা হয়। |
| লকিং স্তর | এটি ইনস্ট্যান্স অথবা ক্লাস লেভেলে লক প্রয়োগ করে। | নির্দিষ্ট অবজেক্ট বা ক্লাসের উপর লক প্রয়োগ করা যায়। |
| লকিং কন্ট্রোল | এটি পুরো মেথডের জন্য স্বয়ংক্রিয়ভাবে লক নিয়ন্ত্রণ করে। | কোড ব্লকটি যে অংশের জন্য প্রয়োজন, সেই অংশে লক নিয়ন্ত্রণ করা হয়। |
| কোড কভারেজ | পুরো মেথডের জন্য লক প্রয়োগ করা হয়, যা নির্দিষ্ট কোডের মধ্যে কাজ করার ক্ষেত্রে অকার্যকর হতে পারে। | প্রয়োজন অনুযায়ী লকিং কভারেজ খুব সীমিত রাখা যায়। |
| পারফরম্যান্স | এটি পুরো মেথডের জন্য লক প্রয়োগ করে, যা অতিরিক্ত কম্পিউটেশনাল কস্ট তৈরি করতে পারে। | শুধুমাত্র কোড ব্লকের জন্য লক প্রয়োগ করে, যা পারফরম্যান্সের জন্য বেশি উপকারী। |
| ব্যবহার | যখন পুরো মেথডটি থ্রেড সেফ করতে হবে। | যখন কিছু নির্দিষ্ট অংশ থ্রেড সেফ করতে হবে। |
- Synchronized মেথড একটি পুরো মেথডে লক প্রয়োগ করে এবং এটি সহজ এবং সরল কেসগুলোর জন্য ভাল কাজ করে।
- Synchronized ব্লক কোডের নির্দিষ্ট অংশে লক প্রয়োগ করে, যা কোডের কার্যকারিতা এবং পারফরম্যান্স উন্নত করতে সাহায্য করে। এটি অধিক নমনীয় এবং থ্রেড সেফ করার জন্য বেশি কন্ট্রোল প্রদান করে।
সুতরাং, আপনি কোনটি ব্যবহার করবেন তা আপনার প্রয়োজনে এবং কোডের কাঠামোর উপর নির্ভর করবে।
Deadlock কী?
ডেডলক (Deadlock) হল একটি পরিস্থিতি যেখানে দুই বা ততোধিক থ্রেড একে অপরের উপর নির্ভরশীল হয়ে একে অপরের সম্পদ (resources) আবদ্ধ করে ফেলে, এবং এই কারণে তারা একে অপরকে মুক্তি দিতে পারে না। এর ফলে থ্রেডগুলি একে অপরকে অপেক্ষা করতে বাধ্য হয় এবং কোন কাজ সম্পাদন করতে পারে না।
ডেডলকের ঘটনার জন্য তিনটি শর্ত পূর্ণ হওয়া প্রয়োজন:
- Mutual Exclusion: অন্তত একটি সম্পদ (resource) এক সময় একটিমাত্র থ্রেডের দ্বারা ব্যবহার করা যেতে পারে।
- Hold and Wait: এক থ্রেড একটি সম্পদ ধারণ করে রেখেছে এবং অন্য সম্পদের জন্য অপেক্ষা করছে।
- No Preemption: থ্রেড থেকে কোন সম্পদ কৌশলে কেড়ে নেয়া যাবে না, থ্রেড নিজে সেই সম্পদটি মুক্ত করবে।
- Circular Wait: একাধিক থ্রেডের মধ্যে একটি চক্র সৃষ্টি হয়, যেখানে এক থ্রেড অন্য থ্রেডের সম্পদ ধরে রেখেছে এবং সেই থ্রেডটি আবার অন্য থ্রেডের সম্পদে আটকে আছে।
Deadlock এর উদাহরণ:
class A {
synchronized void methodA(B b) {
System.out.println("Thread 1: Locked A");
b.last();
}
synchronized void last() {
System.out.println("Inside A's last method");
}
}
class B {
synchronized void methodB(A a) {
System.out.println("Thread 2: Locked B");
a.last();
}
synchronized void last() {
System.out.println("Inside B's last method");
}
}
public class DeadlockExample {
public static void main(String[] args) {
A a = new A();
B b = new B();
// Thread 1
new Thread() {
public void run() {
a.methodA(b);
}
}.start();
// Thread 2
new Thread() {
public void run() {
b.methodB(a);
}
}.start();
}
}
এখানে, প্রথম থ্রেড A ক্লাসের methodA() মেথডে B ক্লাসের last() মেথড কল করার জন্য আটকেছে, এবং দ্বিতীয় থ্রেড B ক্লাসের methodB() মেথডে A ক্লাসের last() মেথড কল করতে গিয়ে আটকে আছে। এই পরিস্থিতি ডেডলক তৈরি করে।
Deadlock প্রতিরোধের উপায়:
ডেডলক প্রতিরোধে কয়েকটি কৌশল ব্যবহার করা যেতে পারে:
- লক অর্ডার (Lock Ordering):
- সব থ্রেডদের জন্য একটি নির্দিষ্ট লক অর্ডার নির্ধারণ করুন। এর মাধ্যমে, যখন একাধিক লক প্রয়োজন হয়, তখন থ্রেডগুলো পূর্বনির্ধারিত অর্ডারে লক করবে, যাতে ডেডলক এড়ানো যায়।
- উদাহরণ: প্রথমে
Aলক করুন এবং তারপরBলক করুন। আরেকটি থ্রেড প্রথমেAনা নিয়েBনিয়ে অপেক্ষা করবে না।
- টাইট টাইম আউট (Timeout) ব্যবহার করুন:
- আপনি
ReentrantLockব্যবহার করলে,tryLock()মেথডের মাধ্যমে নির্দিষ্ট সময়ের জন্য লক চেষ্টা করতে পারেন। যদি লক না পাওয়া যায়, তখন থ্রেডটি পুনরায় চেষ্টা করতে পারে বা অন্য কোন ব্যবস্থা নিতে পারে। উদাহরণ:
Lock lock1 = new ReentrantLock(); Lock lock2 = new ReentrantLock(); boolean lock1Acquired = lock1.tryLock(); boolean lock2Acquired = lock2.tryLock(); if (lock1Acquired && lock2Acquired) { // Both locks acquired } else { // Release the locks and retry }
- আপনি
- লক মুক্তি (Lock Release) নিশ্চিত করা:
finallyব্লক ব্যবহার করুন যাতে থ্রেড সম্পদ ব্যবহারের পর লক মুক্ত করতে পারে, এটি নিশ্চিত করে যে কোনো থ্রেড লক ধরে না রেখে ব্লক থেকে বেরিয়ে যাবে।
- ডেডলক ডিটেকশন (Deadlock Detection):
- থ্রেডগুলির মধ্যে ডেডলক ডিটেকশন মেকানিজম তৈরি করা যায়, যেখানে থ্রেডগুলো কিছু সময় পর চেক করবে যে তারা কোন থ্রেডের কাছে আটকে রয়েছে কিনা এবং থ্রেডটি নিজেদের কাজ করার জন্য নিজস্ব লক ফেরত দেবে।
- অর্থাৎ, ডেডলক প্রতিরোধের জন্য থ্রেডে প্রয়োগযোগ্য কিছু শর্ত:
- No Circular Wait: এটি নিশ্চিত করতে থ্রেডগুলির মধ্যে অর্ডার থাকতে হবে।
- No Hold and Wait: থ্রেড যদি একাধিক সম্পদ নিয়ে কাজ করতে থাকে, তবে একে একে সমস্ত সম্পদ গ্রহণ করতে হবে।
- Preemption: অন্যথায়, যদি থ্রেড এক বা একাধিক সম্পদে আটকে থাকে, তবে সেই সম্পদ অন্য থ্রেডের কাছে স্থানান্তরিত করা উচিত।
Deadlock Avoidance এবং Detection:
- Deadlock Avoidance:
- Banker's Algorithm ব্যবহার করে সিস্টেমে থ্রেডগুলির জন্য লক অর্জন করতে সীমিত রিসোর্সগুলো বিতরণ করতে পারে।
- Deadlock Detection:
- থ্রেড চলাকালে, যদি একটি ডেডলক শনাক্ত হয়, তবে সিস্টেমটি এক বা একাধিক থ্রেডকে বাতিল করে এবং থ্রেডগুলো পুনরায় শুরু করতে পারে।
ডেডলক একটি গুরুতর সমস্যা হতে পারে যদি এটি সঠিকভাবে পরিচালিত না হয়, কিন্তু কিছু কৌশল এবং নীতি প্রয়োগের মাধ্যমে এটি প্রতিরোধ করা সম্ভব। সঠিক লক অর্ডার, টাইট টাইম আউট, লক মুক্তি এবং ডেডলক ডিটেকশন কৌশল ব্যবহার করে আপনি আপনার জাভা প্রোগ্রামে ডেডলক থেকে বিরত থাকতে পারেন।
Read more