Java Multithreading হল একটি প্রোগ্রামিং প্যারাডাইম যেখানে একসাথে একাধিক কাজ বা থ্রেড কার্যকরভাবে চলতে পারে। এটি Java তে concurrent execution অর্জন করতে ব্যবহৃত হয়, যার ফলে আপনি আপনার প্রোগ্রামের কর্মক্ষমতা উন্নত করতে পারেন। Multithreading ব্যবহার করে CPU-এর সম্পূর্ণ ক্ষমতা ব্যবহার করতে পারেন এবং দীর্ঘ-running কাজের পারফরম্যান্স উন্নত করতে সাহায্য করে।
Java তে Multithreading তৈরি করার জন্য দুটি সাধারণ উপায় রয়েছে:
- Thread Class এক্সটেন্ড করা
- Runnable Interface ইমপ্লিমেন্ট করা
নিচে Multithreading এর উদাহরণ দেওয়া হয়েছে।
1. Thread Class Extending Example
এখানে, আমরা Thread ক্লাসটিকে এক্সটেন্ড করে একটি নতুন থ্রেড তৈরি করবো।
// Thread class extending example
class MyThread extends Thread {
@Override
public void run() {
// Thread performing task
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getId() + " Value: " + i);
}
}
}
public class ThreadExample {
public static void main(String[] args) {
// Creating thread objects
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
// Starting threads
t1.start();
t2.start();
}
}
Output (May vary based on thread scheduling):
1 Value: 1
2 Value: 1
1 Value: 2
2 Value: 2
1 Value: 3
2 Value: 3
1 Value: 4
2 Value: 4
1 Value: 5
2 Value: 5
Code Explanation:
- Thread Class:
MyThreadক্লাসটিThreadক্লাসটিকে এক্সটেন্ড করেছে এবংrun()মেথডটি ওভাররাইড করেছে। এটি থ্রেডের কার্য সম্পাদন করার জন্য কল হয়। - start() method:
start()মেথড থ্রেড চালু করার জন্য ব্যবহার করা হয়, এটি একটি নতুন থ্রেড তৈরি করে এবং তা রান করার জন্য সিস্টেমকে জানায়। - currentThread().getId(): এটি থ্রেডের আইডি বের করতে ব্যবহৃত হয় যাতে বুঝা যায় কোন থ্রেড বর্তমানে কার্যকর।
2. Runnable Interface Implementing Example
এখানে আমরা Runnable Interface ব্যবহার করে একটি থ্রেড তৈরি করবো। Runnable একটি ফাংশনাল ইন্টারফেস, যা run() মেথড ডিফাইন করে।
// Runnable interface implementing example
class MyRunnable implements Runnable {
@Override
public void run() {
// Thread performing task
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getId() + " Value: " + i);
}
}
}
public class RunnableExample {
public static void main(String[] args) {
// Creating MyRunnable object
MyRunnable myRunnable = new MyRunnable();
// Creating Thread objects and passing MyRunnable object
Thread t1 = new Thread(myRunnable);
Thread t2 = new Thread(myRunnable);
// Starting threads
t1.start();
t2.start();
}
}
Output (May vary based on thread scheduling):
1 Value: 1
2 Value: 1
1 Value: 2
2 Value: 2
1 Value: 3
2 Value: 3
1 Value: 4
2 Value: 4
1 Value: 5
2 Value: 5
Code Explanation:
- Runnable Interface:
MyRunnableক্লাসটিRunnableইন্টারফেস ইমপ্লিমেন্ট করেছে এবংrun()মেথডকে ডিফাইন করেছে। এটি থ্রেডের কার্য সম্পাদন করবে। - Thread class:
Threadক্লাসের মাধ্যমেRunnableইন্টারফেসের অবজেক্টটি থ্রেডে রুপান্তরিত করা হয়েছে।
3. Multithreading with Sleep Method Example
এখানে আমরা Thread.sleep() মেথড ব্যবহার করবো, যা থ্রেডকে নির্দিষ্ট সময়ের জন্য বিরতি দেওয়ার কাজ করে।
class MyThread extends Thread {
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getId() + " Value: " + i);
try {
// Sleep for 1000 milliseconds (1 second)
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ThreadSleepExample {
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.start();
t2.start();
}
}
Output (May vary based on thread scheduling and sleep time):
1 Value: 1
2 Value: 1
1 Value: 2
2 Value: 2
1 Value: 3
2 Value: 3
1 Value: 4
2 Value: 4
1 Value: 5
2 Value: 5
Code Explanation:
- Thread.sleep(1000):
Thread.sleep()মেথড থ্রেডকে 1000 মিলিসেকেন্ড (1 সেকেন্ড) পর্যন্ত বিরতি দেয়। এটি অন্য থ্রেডকে প্রসেস করার সুযোগ দেয় এবং থ্রেডের কার্যকারিতা উন্নত করতে সাহায্য করে।
4. Daemon Thread Example
Daemon thread হল এমন থ্রেড যা মূল থ্রেডের কাজ শেষ হওয়ার পর স্বয়ংক্রিয়ভাবে শেষ হয়ে যায়। এটি ব্যবহার করা হয় পৃষ্ঠাভূমি কাজ বা ব্যাকগ্রাউন্ড কাজ পরিচালনা করতে।
class DaemonThread extends Thread {
@Override
public void run() {
while (true) {
System.out.println(Thread.currentThread().getName() + " is running.");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class DaemonThreadExample {
public static void main(String[] args) {
DaemonThread thread = new DaemonThread();
// Set this thread as daemon
thread.setDaemon(true);
thread.start();
// Main thread execution
try {
Thread.sleep(2000); // Main thread sleeps for 2 seconds
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Main thread finished.");
}
}
Output:
main is running.
main is running.
main is running.
Main thread finished.
Code Explanation:
- Daemon Thread:
thread.setDaemon(true)ব্যবহার করে থ্রেডটিকে daemon thread হিসেবে সেট করা হয়েছে। - Daemon threads: মূল থ্রেডের কাজ শেষ হলে তাদের কাজও বন্ধ হয়ে যায়, এবং তারা কোনো রিসোর্স আটকে রাখে না।
5. Thread Synchronization Example
একাধিক থ্রেড যদি একে অপরের উপর নির্ভর করে বা একই ডেটা ব্যবহার করে, তবে thread synchronization ব্যবহার করা প্রয়োজন যাতে ডেটা ইন্টেগ্রিটি বজায় থাকে।
class Counter {
private int count = 0;
// Synchronized method to prevent thread interference
synchronized void increment() {
count++;
}
synchronized int getCount() {
return count;
}
}
class MyThread extends Thread {
Counter counter;
MyThread(Counter counter) {
this.counter = counter;
}
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
counter.increment();
System.out.println(Thread.currentThread().getName() + " Count: " + counter.getCount());
}
}
}
public class ThreadSynchronizationExample {
public static void main(String[] args) {
Counter counter = new Counter();
MyThread t1 = new MyThread(counter);
MyThread t2 = new MyThread(counter);
t1.start();
t2.start();
}
}
Output:
Thread-0 Count: 1
Thread-1 Count: 2
Thread-0 Count: 3
Thread-1 Count: 4
Thread-0 Count: 5
Thread-1 Count: 6
Code Explanation:
- Synchronized Method:
increment()এবংgetCount()মেথডগুলিকে synchronized করা হয়েছে যাতে একে একে থ্রেডগুলো ডেটা অ্যাক্সেস করতে পারে এবং কোনো সমস্যা না হয়।
Java তে multithreading এর মাধ্যমে আপনি একাধিক কাজকে একসাথে পরিচালনা করতে পারেন এবং কর্মক্ষমতা বাড়াতে পারেন। বিভিন্ন পদ্ধতি যেমন Thread Class, Runnable Interface, Synchronization, Daemon Threads ইত্যাদি ব্যবহার করে আপনি আরও কার্যকরী এবং সুরক্ষিত মাল্টি-থ্রেডেড প্রোগ্রাম তৈরি করতে
পারবেন।
Java তে Thread Creation এবং Runnable Interface ব্যবহার করা একটি সাধারণ প্র্যাকটিস যা multithreading প্রোগ্রামিংয়ের মাধ্যমে একাধিক কার্যক্রম (tasks) একসাথে বা параллেলভাবে চালানোর সুবিধা দেয়। Thread হল একটি সম্পূর্ণ কর্মক্ষম একক যা CPU এর মাধ্যমে একযোগভাবে কাজ করতে পারে। Java তে Thread তৈরি করার দুটি প্রধান উপায় আছে:
- Thread Class এক্সটেন্ড করে।
- Runnable Interface ইমপ্লিমেন্ট করে।
এখানে আমরা Runnable Interface ব্যবহার করে Thread তৈরি করার উদাহরণ দেখাবো।
Runnable Interface ব্যবহার করে Thread তৈরি
Runnable Interface হল একটি ফাংশনাল ইন্টারফেস, যা একটি মাত্র run() মেথড নিয়ে কাজ করে। আপনি Runnable ইন্টারফেস ইমপ্লিমেন্ট করে একটি ক্লাস তৈরি করতে পারেন, যা থ্রেডের কার্যকারিতা (logic) কন্ট্রোল করবে।
ধাপ 1: Runnable Interface ইমপ্লিমেন্ট করা
// ThreadExample.java
public class ThreadExample implements Runnable {
// run() method is the entry point for the thread
@Override
public void run() {
// Define the task that the thread will execute
System.out.println(Thread.currentThread().getId() + " is running the task.");
}
public static void main(String[] args) {
// Create an object of the class that implements Runnable
ThreadExample task = new ThreadExample();
// Create two threads to run the same task
Thread thread1 = new Thread(task);
Thread thread2 = new Thread(task);
// Start both threads
thread1.start();
thread2.start();
// Print the main thread's information
System.out.println(Thread.currentThread().getId() + " is the main thread.");
}
}
কোড ব্যাখ্যা:
RunnableInterface ইমপ্লিমেন্ট করা:ThreadExampleক্লাসটিRunnableইন্টারফেস ইমপ্লিমেন্ট করছে এবং এর মধ্যেrun()মেথড ডিফাইন করা হয়েছে।run()মেথড হলো সেই মেথড যা থ্রেড চালানোর সময় কার্যকরী হবে।
- Thread Creation:
Thread thread1 = new Thread(task);— এখানেtaskহচ্ছেRunnableইন্টারফেস ইমপ্লিমেন্ট করা একটি অবজেক্ট।Threadক্লাসের কনস্ট্রাক্টরেRunnableঅবজেক্ট দেয়া হয়, যার মাধ্যমে থ্রেডটিrun()মেথডটি চালাবে।
- Thread Start:
thread1.start();এবংthread2.start();—start()মেথডটি থ্রেডকে কার্যকর করে, অর্থাৎ এটি থ্রেডের কার্যক্রম শুরু করবে। একাধিক থ্রেড একইRunnableঅবজেক্ট ব্যবহার করতে পারে, কিন্তু তারা নিজেদের নিজস্ব execution context এ কার্যকর হবে।
- Main Thread:
System.out.println(Thread.currentThread().getId() + " is the main thread.");— এটি মূল থ্রেড (main thread) এর আইডি এবং নাম প্রিন্ট করে, যেটি সবথেকে প্রথমে কার্যকরী হয়।
উদাহরণ আউটপুট:
1 is the main thread.
12 is running the task.
13 is running the task.
ব্যাখ্যা:
1 is the main thread.মূল থ্রেডের ID প্রিন্ট করা হয়েছে।12এবং13হল নতুন থ্রেডের ID যেগুলি কার্যকরী হয়ে তাদের respective কাজ চালাচ্ছে। এটি প্রতিটি থ্রেডেরrun()মেথডের কাজ করার সময় প্রদর্শিত হয়।
ধাপ 2: Thread Class ব্যবহার করা (Alternating Method)
এছাড়া আপনি Thread Class এক্সটেন্ড করে নিজের থ্রেড তৈরি করতে পারেন, তবে সাধারণত Runnable Interface ব্যবহার করা হয় কারণ এটি একাধিক ক্লাসকে একসাথে একটি থ্রেডের কার্যকারিতা ব্যবহার করার সুযোগ দেয়।
// ThreadClassExample.java
public class ThreadClassExample extends Thread {
@Override
public void run() {
// Define the task that the thread will execute
System.out.println(Thread.currentThread().getId() + " is executing its task.");
}
public static void main(String[] args) {
// Create two threads using the extended Thread class
ThreadClassExample thread1 = new ThreadClassExample();
ThreadClassExample thread2 = new ThreadClassExample();
// Start both threads
thread1.start();
thread2.start();
// Print the main thread's information
System.out.println(Thread.currentThread().getId() + " is the main thread.");
}
}
- Thread Creation Java তে Runnable Interface ব্যবহার করে করা হয়, যেখানে আপনি একটি
run()মেথড ডিফাইন করেন এবং পরেThreadক্লাসের মাধ্যমে তাকে কার্যকরী করেন। - Runnable Interface এবং Thread Class দুটি পদ্ধতিই কার্যকরী হলেও, সাধারণভাবে Runnable Interface ব্যবহৃত হয়, কারণ এটি একাধিক ক্লাসকে একসাথে থ্রেড কার্যকর করার সুযোগ দেয়।
- Java তে multithreading আপনার প্রোগ্রামে একাধিক কাজ সমান্তরালে চলানোর ক্ষমতা প্রদান করে, যা অ্যাপ্লিকেশন পারফরম্যান্স বৃদ্ধি করতে সহায়ক।
Thread synchronization in Java ensures that only one thread can access a critical section of code at a time, thus preventing data inconsistency when multiple threads are involved.
Here is an example demonstrating thread synchronization in Java where multiple threads access a shared resource.
Java Example: Thread Synchronization
class Counter {
private int count = 0;
// Synchronized method to ensure only one thread can access this at a time
public synchronized void increment() {
count++;
}
public int getCount() {
return count;
}
}
class MyThread extends Thread {
private Counter counter;
public MyThread(Counter counter) {
this.counter = counter;
}
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
}
}
public class SynchronizationExample {
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
// Creating two threads that will access the same Counter object
MyThread thread1 = new MyThread(counter);
MyThread thread2 = new MyThread(counter);
// Starting both threads
thread1.start();
thread2.start();
// Waiting for both threads to finish
thread1.join();
thread2.join();
// Printing the final count
System.out.println("Final count: " + counter.getCount());
}
}
Explanation:
- Counter class: This class contains a shared resource (
count). The methodincrement()is synchronized to ensure that only one thread can increment thecountat a time. - MyThread class: This thread class performs the increment operation on the
Counterobject. - Synchronization: The
increment()method is synchronized, meaning when one thread is executing it, other threads must wait until the current thread finishes. - Main method: Two threads (
thread1andthread2) are created and started. After both threads finish executing, the final value ofcountis printed.
Output Example:
Final count: 2000
Without synchronization, there could be a race condition where multiple threads update count simultaneously, leading to incorrect results. The synchronized keyword prevents this issue by allowing only one thread to execute the critical section at a time.
জাভা ইন্টার-থ্রেড কমিউনিকেশনের জন্য wait(), notify(), এবং notifyAll() মেথড সরবরাহ করে যা Object ক্লাসের অংশ। এই মেথডগুলো থ্রেডগুলোকে একে অপরের সাথে যোগাযোগ করতে সাহায্য করে, বিশেষ করে যখন তারা শেয়ারড রিসোর্সের সাথে কাজ করছে। নিচে একটি উদাহরণ দেওয়া হল, যেখানে দুটি থ্রেড (প্রোডিউসার এবং কনজিউমার) একটি আইটেম উৎপাদন এবং ভক্ষণ করছে এবং তাদের মধ্যে যোগাযোগ হচ্ছে wait() এবং notify() মেথড ব্যবহার করে।
উদাহরণ: প্রোডিউসার-কনজিউমার সমস্যা
ব্যাখ্যা:
wait()মেথড থ্রেডটিকে লক মুক্ত করে দেয় এবং ওয়েটিং স্টেটে পাঠিয়ে দেয়।notify()মেথড একটি থ্রেডকে জাগিয়ে তোলে যে থ্রেডটি ওয়েটিং স্টেটে ছিল।notifyAll()মেথড সব ওয়েটিং থ্রেডকে জাগিয়ে তোলে।
নিচে একটি প্রোডিউসার-কনজিউমার পরিস্থিতির উদাহরণ দেওয়া হল:
class SharedResource {
private int item = 0;
// এই মেথডটি প্রোডিউসার থ্রেড দ্বারা আইটেম উৎপাদনের জন্য কল করা হয়
public synchronized void produce() throws InterruptedException {
while (item != 0) {
wait(); // আইটেম কনজিউম হওয়ার জন্য অপেক্ষা করে
}
item++;
System.out.println("Produced item: " + item);
notify(); // কনজিউমারকে জানান যে একটি আইটেম উৎপাদিত হয়েছে
}
// এই মেথডটি কনজিউমার থ্রেড দ্বারা আইটেম ভক্ষণ করার জন্য কল করা হয়
public synchronized void consume() throws InterruptedException {
while (item == 0) {
wait(); // আইটেম না থাকার জন্য অপেক্ষা করে
}
System.out.println("Consumed item: " + item);
item--;
notify(); // প্রোডিউসারকে জানান যে আইটেম ভক্ষণ করা হয়েছে
}
}
class Producer implements Runnable {
SharedResource resource;
public Producer(SharedResource resource) {
this.resource = resource;
}
@Override
public void run() {
try {
while (true) {
resource.produce();
Thread.sleep(1000); // 1 সেকেন্ড ঘুমায় যাতে উৎপাদনের সময় সিমুলেট করা যায়
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Consumer implements Runnable {
SharedResource resource;
public Consumer(SharedResource resource) {
this.resource = resource;
}
@Override
public void run() {
try {
while (true) {
resource.consume();
Thread.sleep(1500); // 1.5 সেকেন্ড ঘুমায় যাতে ভক্ষণ সময় সিমুলেট করা যায়
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class InterThreadCommunicationExample {
public static void main(String[] args) {
SharedResource resource = new SharedResource();
Thread producerThread = new Thread(new Producer(resource));
Thread consumerThread = new Thread(new Consumer(resource));
producerThread.start();
consumerThread.start();
}
}
কোডের ব্যাখ্যা:
- SharedResource ক্লাস:
- এই ক্লাসটি শেয়ারড রিসোর্স (আইটেম) প্রতিনিধিত্ব করে।
- এর দুটি মেথড রয়েছে:
produce()এবংconsume(), যেগুলো সিঙ্ক্রোনাইজড যাতে একসময় কেবল একটি থ্রেড রিসোর্সটি অ্যাক্সেস করতে পারে। produce()মেথডটি অপেক্ষা করবে যদি আইটেমটি কনজিউম না হয় (অর্থাৎ,item != 0), আরconsume()মেথডটি অপেক্ষা করবে যদি কোনো আইটেম না থাকে কনজিউম করার জন্য (অর্থাৎ,item == 0)।- একটি আইটেম উৎপাদন করার পরে,
produce()মেথডnotify()কল করে কনজিউমার থ্রেডকে জাগিয়ে তোলে। - একটি আইটেম কনজিউম করার পরে,
consume()মেথডnotify()কল করে প্রোডিউসার থ্রেডকে জাগিয়ে তোলে।
- Producer এবং Consumer ক্লাস:
ProducerএবংConsumerক্লাস দুটিRunnableইন্টারফেস ইমপ্লিমেন্ট করে এবং তাদেরrun()মেথডে অবিরাম আইটেম উৎপাদন এবং কনজিউম করার কাজ করে।Producer1 সেকেন্ডের জন্য ঘুমায় আইটেম উৎপাদনের পর, এবংConsumer1.5 সেকেন্ডের জন্য ঘুমায় আইটেম কনজিউম করার পর, যাতে উৎপাদন এবং ভক্ষণ সময় সিমুলেট করা যায়।
- Main ক্লাস:
main()মেথডে,ProducerএবংConsumerক্লাসের ইনস্ট্যান্স তৈরি করা হয় এবং তাদের থ্রেড চালু করা হয়।- থ্রেডগুলো অবিরাম চলতে থাকে, আইটেম উৎপাদন এবং কনজিউম করে।
মূল ধারণাসমূহ:
synchronized: এটি নিশ্চিত করে যে একসময় একটিমাত্র থ্রেডproduce()এবংconsume()মেথড অ্যাক্সেস করতে পারে, যা রেস কন্ডিশন (race condition) প্রতিরোধ করে।wait(): এটি থ্রেডটিকে অপেক্ষায় রাখে যতক্ষণ না অন্য থ্রেড তাকে জাগিয়ে তুলে।notify(): এটি একটি থ্রেডকে জাগিয়ে তোলে যেটি ওয়েটিং অবস্থায় ছিল।notifyAll(): এটি সব ওয়েটিং থ্রেডকে জাগিয়ে তোলে। (এই উদাহরণেnotifyAll()ব্যবহার করা হয়নি, তবে এটি যখন একাধিক থ্রেড থাকে তখন প্রয়োজন হতে পারে।)
এই উদাহরণটি দেখায় কিভাবে ইন্টার-থ্রেড কমিউনিকেশন ব্যবহার করে থ্রেডগুলো একে অপরের সাথে যোগাযোগ করতে পারে এবং সিঙ্ক্রোনাইজডভাবে শেয়ারড রিসোর্সের সাথে কাজ করতে পারে।
ডেডলক হচ্ছে একটি পরিস্থিতি যেখানে দুটি বা ততোধিক থ্রেড একে অপরকে ব্লক করে রেখে এমন একটি অবস্থা সৃষ্টি করে, যেখানে তারা সবাই একে অপরের জন্য অপেক্ষা করছে। এতে, কোনো থ্রেডই সম্পূর্ণ হতে পারে না, এবং সিস্টেম "হ্যাং" হয়ে পড়ে।
এটি সাধারাণত ঘটে যখন একাধিক থ্রেড একে অপরের মধ্যে একই রিসোর্সের জন্য অপেক্ষা করছে এবং তারা একে অপরকে ওই রিসোর্স মুক্ত করার জন্য বাধা সৃষ্টি করে।
ডেডলক উদাহরণ:
নিচে একটি উদাহরণ দেওয়া হয়েছে যেখানে দুটি থ্রেড একে অপরের জন্য রিসোর্স ব্লক করছে এবং ডেডলক তৈরি হচ্ছে।
public class DeadlockExample {
// প্রথম রিসোর্স
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("Thread 1: Locked resource 1");
// দ্বিতীয় রিসোর্সের জন্য অপেক্ষা
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (resource2) {
System.out.println("Thread 1: Locked resource 2");
}
}
});
// দ্বিতীয় থ্রেড
Thread thread2 = new Thread(() -> {
synchronized (resource2) {
System.out.println("Thread 2: Locked resource 2");
// প্রথম রিসোর্সের জন্য অপেক্ষা
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (resource1) {
System.out.println("Thread 2: Locked resource 1");
}
}
});
thread1.start();
thread2.start();
}
}
কোড ব্যাখ্যা:
এই উদাহরণে, thread1 প্রথমে resource1 লক করে এবং পরে resource2 লক করতে চায়। অপরদিকে, thread2 প্রথমে resource2 লক করে এবং পরে resource1 লক করতে চায়। এখন, thread1 resource2 লক করার জন্য অপেক্ষা করছে, এবং thread2 resource1 লক করার জন্য অপেক্ষা করছে। তাই দুটি থ্রেড একে অপরকে ব্লক করে, এবং ডেডলক সৃষ্টি হয়।
ডেডলক প্রতিরোধ:
ডেডলক প্রতিরোধের জন্য বেশ কয়েকটি পদ্ধতি রয়েছে। নিচে কিছু পদ্ধতির বর্ণনা দেওয়া হল:
১. ডেডলক ফ্রি লকিং অর্ডার:
ডেডলক প্রতিরোধ করার জন্য সর্বদা রিসোর্সগুলোর জন্য একটি নির্দিষ্ট লকিং অর্ডার অনুসরণ করা উচিত। যদি সব থ্রেড একই অর্ডারে রিসোর্স লক করে, তবে ডেডলক হওয়ার সম্ভাবনা কমে যাবে।
উদাহরণ:
public class DeadlockPrevention {
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("Thread 1: Locked resource 1");
synchronized (resource2) {
System.out.println("Thread 1: Locked resource 2");
}
}
});
Thread thread2 = new Thread(() -> {
synchronized (resource1) {
System.out.println("Thread 2: Locked resource 1");
synchronized (resource2) {
System.out.println("Thread 2: Locked resource 2");
}
}
});
thread1.start();
thread2.start();
}
}
এখানে, আমরা নিশ্চিত করেছি যে thread1 এবং thread2 উভয়েই প্রথম resource1 লক করবে এবং পরে resource2 লক করবে, ফলে ডেডলক এড়ানো যাবে।
২. টাইমআউট ব্যবহার:
ডেডলক এড়ানোর জন্য কিছু থ্রেড একটি নির্দিষ্ট সময় পর লকিং অ্যাকশনটি ত্যাগ করতে পারে। এটি ডেডলক পরিস্থিতি এড়াতে সাহায্য করে।
public class DeadlockWithTimeout {
private static final Object resource1 = new Object();
private static final Object resource2 = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
try {
if (tryLock(resource1, 1000) && tryLock(resource2, 1000)) {
System.out.println("Thread 1: Locked both resources");
} else {
System.out.println("Thread 1: Could not acquire both resources");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread thread2 = new Thread(() -> {
try {
if (tryLock(resource2, 1000) && tryLock(resource1, 1000)) {
System.out.println("Thread 2: Locked both resources");
} else {
System.out.println("Thread 2: Could not acquire both resources");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread1.start();
thread2.start();
}
private static boolean tryLock(Object resource, long timeout) throws InterruptedException {
synchronized (resource) {
return true;
}
}
}
এখানে, tryLock মেথড ব্যবহার করে, থ্রেড একটি নির্দিষ্ট সময় পর রিসোর্স অ্যাক্সেস চেষ্টা করে এবং সফল না হলে একটি ত্রুটি বার্তা প্রিন্ট করে।
৩. ডেডলক ডিটেকশন:
ডেডলক সনাক্ত করতে একটি মনিটরিং সিস্টেম তৈরি করা যেতে পারে যা থ্রেডদের সিস্টেমে থাকা অবস্থার উপর নজর রাখে এবং ডেডলক ঘটলে সিস্টেমকে রিসেট করে।
ডেডলক একটি সাধারণ সমস্যা হতে পারে, তবে উপযুক্ত পরিকল্পনা ও ডিজাইন কৌশল ব্যবহার করে এটি প্রতিরোধ করা সম্ভব। সঠিক লকিং অর্ডার অনুসরণ, টাইমআউট ব্যবহার, এবং ডেডলক সনাক্তকরণের মাধ্যমে সিস্টেমের কর্মক্ষমতা বজায় রাখা সম্ভব।
Thread Pool ব্যবহার করে Multithreading-এর কার্যক্ষমতা বৃদ্ধি করা হয়, যেখানে একাধিক থ্রেড একসাথে কাজ করে, কিন্তু একটি থ্রেড পুল ব্যবস্থাপনা করে থ্রেড তৈরি ও পরিচালনা করে। এর ফলে থ্রেডের অধিক ব্যবহার এবং রিসোর্সের অপচয় কমানো যায়।
নিচে একটি Thread Pool Example দেওয়া হলো যা ExecutorService এবং ExecutorService.submit() ব্যবহার করে থ্রেড পুল তৈরি এবং ব্যবহারের পদ্ধতি দেখাবে।
import java.util.concurrent.*;
public class ThreadPoolExample {
public static void main(String[] args) {
// Thread Pool এর আকার ৩টি থ্রেডের জন্য নির্ধারণ করা হয়েছে
int numberOfThreads = 3;
ExecutorService executorService = Executors.newFixedThreadPool(numberOfThreads);
// Runnable টাস্ক তৈরি করা
Runnable task1 = new Task("Task 1");
Runnable task2 = new Task("Task 2");
Runnable task3 = new Task("Task 3");
Runnable task4 = new Task("Task 4");
// Thread Pool এ টাস্কগুলো সাবমিট করা
executorService.submit(task1);
executorService.submit(task2);
executorService.submit(task3);
executorService.submit(task4);
// Thread Pool বন্ধ করা
executorService.shutdown();
}
// Runnable ইন্টারফেস বাস্তবায়ন করা
static class Task implements Runnable {
private String taskName;
public Task(String taskName) {
this.taskName = taskName;
}
@Override
public void run() {
// প্রতিটি টাস্কের কাজ
try {
System.out.println(taskName + " শুরু হয়েছে, থ্রেড ID: " + Thread.currentThread().getId());
// কিছু সময় অপেক্ষা করা (সীমিত সময়ের জন্য)
Thread.sleep(2000);
System.out.println(taskName + " শেষ হয়েছে, থ্রেড ID: " + Thread.currentThread().getId());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Explanation:
- ExecutorService: এটি থ্রেড পুল তৈরি এবং পরিচালনার জন্য ব্যবহৃত হয়। এখানে,
Executors.newFixedThreadPool()ব্যবহার করে থ্রেড পুলের আকার ৩টি থ্রেড হিসাবে নির্ধারণ করা হয়েছে। অর্থাৎ একসময় তিনটি থ্রেড কাজ করতে পারবে। - Runnable Task:
Runnableইন্টারফেসেrun()মেথডের মধ্যে কাজের কোড থাকে। এখানে, প্রতিটিTaskক্লাসেরrun()মেথডে কিছু সময় অপেক্ষা (Thread.sleep()) এবং প্রিন্ট স্টেটমেন্ট দেওয়া হয়েছে যা প্রতিটি টাস্কের শুরু এবং শেষ সময় দেখায়। - submit() Method:
submit()মেথডের মাধ্যমে আমরা টাস্কগুলো থ্রেড পুলে জমা দিচ্ছি। এর ফলে, থ্রেড পুল অব্যাহতভাবে থ্রেডগুলোকে ব্যবহার করে টাস্কগুলো সম্পাদন করবে। - shutdown() Method:
shutdown()মেথড দিয়ে থ্রেড পুল বন্ধ করা হয়, অর্থাৎ কোন নতুন টাস্ক গ্রহণ করা হবে না, কিন্তু যেগুলো ইতিমধ্যে পুলে আছে সেগুলো সম্পন্ন হবে।
Output Example:
Task 1 শুরু হয়েছে, থ্রেড ID: 10
Task 2 শুরু হয়েছে, থ্রেড ID: 11
Task 3 শুরু হয়েছে, থ্রেড ID: 12
Task 1 শেষ হয়েছে, থ্রেড ID: 10
Task 2 শেষ হয়েছে, থ্রেড ID: 11
Task 3 শেষ হয়েছে, থ্রেড ID: 12
Task 4 শুরু হয়েছে, থ্রেড ID: 10
Task 4 শেষ হয়েছে, থ্রেড ID: 10
এখানে, টাস্কগুলি একাধিক থ্রেড দ্বারা প্রসেস করা হচ্ছে, এবং থ্রেড পুলের মাধ্যমে থ্রেডগুলোর পুনরায় ব্যবহার করা হচ্ছে, যার ফলে কার্যক্ষমতা বৃদ্ধি পাচ্ছে এবং নতুন থ্রেড তৈরির সময় এবং রিসোর্সের অপচয় কমানো হচ্ছে।
Read more