Java Multithreading হল একটি প্রোগ্রামিং প্যারাডাইম যেখানে একসাথে একাধিক কাজ বা থ্রেড কার্যকরভাবে চলতে পারে। এটি Java তে concurrent execution অর্জন করতে ব্যবহৃত হয়, যার ফলে আপনি আপনার প্রোগ্রামের কর্মক্ষমতা উন্নত করতে পারেন। Multithreading ব্যবহার করে CPU-এর সম্পূর্ণ ক্ষমতা ব্যবহার করতে পারেন এবং দীর্ঘ-running কাজের পারফরম্যান্স উন্নত করতে সাহায্য করে।
Java তে Multithreading তৈরি করার জন্য দুটি সাধারণ উপায় রয়েছে:
নিচে Multithreading এর উদাহরণ দেওয়া হয়েছে।
এখানে, আমরা 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
MyThread
ক্লাসটি Thread
ক্লাসটিকে এক্সটেন্ড করেছে এবং run()
মেথডটি ওভাররাইড করেছে। এটি থ্রেডের কার্য সম্পাদন করার জন্য কল হয়।start()
মেথড থ্রেড চালু করার জন্য ব্যবহার করা হয়, এটি একটি নতুন থ্রেড তৈরি করে এবং তা রান করার জন্য সিস্টেমকে জানায়।এখানে আমরা 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
MyRunnable
ক্লাসটি Runnable
ইন্টারফেস ইমপ্লিমেন্ট করেছে এবং run()
মেথডকে ডিফাইন করেছে। এটি থ্রেডের কার্য সম্পাদন করবে।Thread
ক্লাসের মাধ্যমে Runnable
ইন্টারফেসের অবজেক্টটি থ্রেডে রুপান্তরিত করা হয়েছে।এখানে আমরা 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
Thread.sleep()
মেথড থ্রেডকে 1000 মিলিসেকেন্ড (1 সেকেন্ড) পর্যন্ত বিরতি দেয়। এটি অন্য থ্রেডকে প্রসেস করার সুযোগ দেয় এবং থ্রেডের কার্যকারিতা উন্নত করতে সাহায্য করে।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.
thread.setDaemon(true)
ব্যবহার করে থ্রেডটিকে daemon thread হিসেবে সেট করা হয়েছে।একাধিক থ্রেড যদি একে অপরের উপর নির্ভর করে বা একই ডেটা ব্যবহার করে, তবে 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
increment()
এবং getCount()
মেথডগুলিকে synchronized করা হয়েছে যাতে একে একে থ্রেডগুলো ডেটা অ্যাক্সেস করতে পারে এবং কোনো সমস্যা না হয়।Java তে multithreading এর মাধ্যমে আপনি একাধিক কাজকে একসাথে পরিচালনা করতে পারেন এবং কর্মক্ষমতা বাড়াতে পারেন। বিভিন্ন পদ্ধতি যেমন Thread Class, Runnable Interface, Synchronization, Daemon Threads ইত্যাদি ব্যবহার করে আপনি আরও কার্যকরী এবং সুরক্ষিত মাল্টি-থ্রেডেড প্রোগ্রাম তৈরি করতে
পারবেন।
Java তে Thread Creation এবং Runnable Interface ব্যবহার করা একটি সাধারণ প্র্যাকটিস যা multithreading প্রোগ্রামিংয়ের মাধ্যমে একাধিক কার্যক্রম (tasks) একসাথে বা параллেলভাবে চালানোর সুবিধা দেয়। Thread হল একটি সম্পূর্ণ কর্মক্ষম একক যা CPU এর মাধ্যমে একযোগভাবে কাজ করতে পারে। Java তে Thread তৈরি করার দুটি প্রধান উপায় আছে:
এখানে আমরা Runnable Interface ব্যবহার করে Thread তৈরি করার উদাহরণ দেখাবো।
Runnable Interface হল একটি ফাংশনাল ইন্টারফেস, যা একটি মাত্র run()
মেথড নিয়ে কাজ করে। আপনি Runnable ইন্টারফেস ইমপ্লিমেন্ট করে একটি ক্লাস তৈরি করতে পারেন, যা থ্রেডের কার্যকারিতা (logic) কন্ট্রোল করবে।
// 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.");
}
}
Runnable
Interface ইমপ্লিমেন্ট করা:ThreadExample
ক্লাসটি Runnable
ইন্টারফেস ইমপ্লিমেন্ট করছে এবং এর মধ্যে run()
মেথড ডিফাইন করা হয়েছে। run()
মেথড হলো সেই মেথড যা থ্রেড চালানোর সময় কার্যকরী হবে।Thread thread1 = new Thread(task);
— এখানে task
হচ্ছে Runnable
ইন্টারফেস ইমপ্লিমেন্ট করা একটি অবজেক্ট।Thread
ক্লাসের কনস্ট্রাক্টরে Runnable
অবজেক্ট দেয়া হয়, যার মাধ্যমে থ্রেডটি run()
মেথডটি চালাবে।thread1.start();
এবং thread2.start();
— start()
মেথডটি থ্রেডকে কার্যকর করে, অর্থাৎ এটি থ্রেডের কার্যক্রম শুরু করবে। একাধিক থ্রেড একই Runnable
অবজেক্ট ব্যবহার করতে পারে, কিন্তু তারা নিজেদের নিজস্ব execution context এ কার্যকর হবে।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()
মেথডের কাজ করার সময় প্রদর্শিত হয়।এছাড়া আপনি 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.");
}
}
run()
মেথড ডিফাইন করেন এবং পরে Thread
ক্লাসের মাধ্যমে তাকে কার্যকরী করেন।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.
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());
}
}
count
). The method increment()
is synchronized to ensure that only one thread can increment the count
at a time.Counter
object.increment()
method is synchronized, meaning when one thread is executing it, other threads must wait until the current thread finishes.thread1
and thread2
) are created and started. After both threads finish executing, the final value of count
is printed.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();
}
}
produce()
এবং consume()
, যেগুলো সিঙ্ক্রোনাইজড যাতে একসময় কেবল একটি থ্রেড রিসোর্সটি অ্যাক্সেস করতে পারে।produce()
মেথডটি অপেক্ষা করবে যদি আইটেমটি কনজিউম না হয় (অর্থাৎ, item != 0
), আর consume()
মেথডটি অপেক্ষা করবে যদি কোনো আইটেম না থাকে কনজিউম করার জন্য (অর্থাৎ, item == 0
)।produce()
মেথড notify()
কল করে কনজিউমার থ্রেডকে জাগিয়ে তোলে।consume()
মেথড notify()
কল করে প্রোডিউসার থ্রেডকে জাগিয়ে তোলে।Producer
এবং Consumer
ক্লাস দুটি Runnable
ইন্টারফেস ইমপ্লিমেন্ট করে এবং তাদের run()
মেথডে অবিরাম আইটেম উৎপাদন এবং কনজিউম করার কাজ করে।Producer
1 সেকেন্ডের জন্য ঘুমায় আইটেম উৎপাদনের পর, এবং Consumer
1.5 সেকেন্ডের জন্য ঘুমায় আইটেম কনজিউম করার পর, যাতে উৎপাদন এবং ভক্ষণ সময় সিমুলেট করা যায়।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();
}
}
}
}
Executors.newFixedThreadPool()
ব্যবহার করে থ্রেড পুলের আকার ৩টি থ্রেড হিসাবে নির্ধারণ করা হয়েছে। অর্থাৎ একসময় তিনটি থ্রেড কাজ করতে পারবে।Runnable
ইন্টারফেসে run()
মেথডের মধ্যে কাজের কোড থাকে। এখানে, প্রতিটি Task
ক্লাসের run()
মেথডে কিছু সময় অপেক্ষা (Thread.sleep()
) এবং প্রিন্ট স্টেটমেন্ট দেওয়া হয়েছে যা প্রতিটি টাস্কের শুরু এবং শেষ সময় দেখায়।submit()
মেথডের মাধ্যমে আমরা টাস্কগুলো থ্রেড পুলে জমা দিচ্ছি। এর ফলে, থ্রেড পুল অব্যাহতভাবে থ্রেডগুলোকে ব্যবহার করে টাস্কগুলো সম্পাদন করবে।shutdown()
মেথড দিয়ে থ্রেড পুল বন্ধ করা হয়, অর্থাৎ কোন নতুন টাস্ক গ্রহণ করা হবে না, কিন্তু যেগুলো ইতিমধ্যে পুলে আছে সেগুলো সম্পন্ন হবে।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