জাভা কনকারেন্সিতে উন্নত প্যাটার্ন ও সেরা প্র্যাকটিস ব্যবহার করে অ্যাপ্লিকেশনগুলোর পারফরম্যান্স, রিডেবিলিটি, এবং সঠিকতা উন্নত করা যায়।
Producer-Consumer হলো একটি ক্লাসিক প্যাটার্ন, যেখানে এক বা একাধিক প্রযোজক (Producer) ডেটা উৎপন্ন করে এবং এক বা একাধিক গ্রাহক (Consumer) সেই ডেটা প্রক্রিয়া করে।
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ArrayBlockingQueue;
public class ProducerConsumerExample {
public static void main(String[] args) {
BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);
Runnable producer = () -> {
for (int i = 1; i <= 10; i++) {
try {
queue.put(i);
System.out.println("Produced: " + i);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
};
Runnable consumer = () -> {
try {
while (true) {
Integer value = queue.take();
System.out.println("Consumed: " + value);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
};
new Thread(producer).start();
new Thread(consumer).start();
}
}
Fork/Join Framework কাজগুলো ছোট ছোট টুকরায় ভাগ করে সমান্তরালে এক্সিকিউট করতে ব্যবহার করা হয়।
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.ForkJoinPool;
public class ForkJoinExample extends RecursiveTask<Long> {
private final long start;
private final long end;
private static final long THRESHOLD = 10_000;
public ForkJoinExample(long start, long end) {
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
if (end - start <= THRESHOLD) {
long sum = 0;
for (long i = start; i <= end; i++) {
sum += i;
}
return sum;
} else {
long mid = (start + end) / 2;
ForkJoinExample leftTask = new ForkJoinExample(start, mid);
ForkJoinExample rightTask = new ForkJoinExample(mid + 1, end);
leftTask.fork();
return rightTask.compute() + leftTask.join();
}
}
public static void main(String[] args) {
ForkJoinPool pool = new ForkJoinPool();
ForkJoinExample task = new ForkJoinExample(1, 1_000_000);
System.out.println("Sum: " + pool.invoke(task));
}
}
CompletionService থ্রেডগুলোর কাজের ফলাফল সংগ্রহ এবং পরিচালনা করতে ব্যবহৃত হয়।
import java.util.concurrent.*;
public class CompletionServiceExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(3);
CompletionService<String> service = new ExecutorCompletionService<>(executor);
for (int i = 1; i <= 5; i++) {
final int taskId = i;
service.submit(() -> {
Thread.sleep(1000);
return "Task " + taskId + " completed.";
});
}
executor.shutdown();
for (int i = 0; i < 5; i++) {
try {
Future<String> result = service.take();
System.out.println(result.get());
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Immutable Object প্যাটার্ন ডেটা সিঙ্ক্রোনাইজেশনের প্রয়োজন কমিয়ে পারফরম্যান্স উন্নত করে।
final class ImmutableData {
private final String name;
private final int age;
public ImmutableData(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
একটি থ্রেডের নিজস্ব ডেটা সংরক্ষণ করার জন্য ThreadLocal ব্যবহার করা হয়।
public class ThreadLocalExample {
private static final ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 0);
public static void main(String[] args) {
Runnable task = () -> {
threadLocal.set(threadLocal.get() + 1);
System.out.println(Thread.currentThread().getName() + ": " + threadLocal.get());
};
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
}
}
Thread
তৈরি এড়িয়ে ExecutorService
ব্যবহার করুন।-Xms
, -Xmx
, -XX:+UseG1GC
) ব্যবহার করুন।জাভা কনকারেন্সির Advanced Patterns এবং Best Practices সঠিকভাবে ব্যবহার করলে:
সঠিক প্যাটার্ন নির্বাচন এবং সেরা প্র্যাকটিস মেনে চললে কনকারেন্ট প্রোগ্রামিং আরও নিরাপদ এবং দক্ষ হয়।
Java Concurrency API তে Future
, CompletableFuture
, এবং Executor
মাল্টিথ্রেডেড প্রোগ্রামিং আরও সহজ ও কার্যকর করে। তারা অ্যাসিঙ্ক্রোনাস প্রোগ্রামিং এবং কনকারেন্ট টাস্ক ম্যানেজমেন্টের জন্য উন্নত সমাধান প্রদান করে।
Future
ইন্টারফেস একটি অ্যাসিঙ্ক্রোনাস টাস্কের রেজাল্ট ধরে রাখে, যা ভবিষ্যতে উপলব্ধ হবে।মেথড | বর্ণনা |
---|---|
get() | রেজাল্ট ফেরত দেয়; যদি রেজাল্ট প্রস্তুত না হয়, এটি ব্লক হয়। |
get(long timeout, TimeUnit unit) | নির্দিষ্ট সময় পর্যন্ত অপেক্ষা করে রেজাল্ট রিটার্ন করে। |
isDone() | টাস্ক সম্পন্ন হয়েছে কিনা তা চেক করে। |
cancel(boolean mayInterruptIfRunning) | টাস্ক বাতিল করার জন্য। |
import java.util.concurrent.*;
public class FutureExample {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executor = Executors.newSingleThreadExecutor();
Callable<Integer> task = () -> {
Thread.sleep(2000); // Simulate some work
return 42;
};
Future<Integer> future = executor.submit(task);
System.out.println("Task is submitted. Doing other work...");
// Wait for the result
Integer result = future.get(); // Blocks until the task is complete
System.out.println("Result: " + result);
executor.shutdown();
}
}
get()
মেথড ব্লক করে রাখে যতক্ষণ না রেজাল্ট প্রস্তুত।CompletableFuture
হলো Future-এর একটি উন্নত সংস্করণ যা নন-ব্লকিং এবং চেইনড অ্যাসিঙ্ক্রোনাস টাস্ক পরিচালনা করতে সক্ষম।মেথড | বর্ণনা |
---|---|
supplyAsync(Supplier<T>) | অ্যাসিঙ্ক্রোনাস টাস্ক শুরু করে এবং রেজাল্ট ফেরত দেয়। |
thenApply(Function<T, R>) | রেজাল্টে একটি অপারেশন প্রয়োগ করে। |
thenAccept(Consumer<T>) | রেজাল্ট গ্রহণ করে এবং কিছু করে (রিটার্ন কিছুই নয়)। |
thenCombine(CompletableFuture, BiFunction) | দুইটি CompletableFuture এর রেজাল্ট একত্রিত করে। |
exceptionally(Function<Throwable, T>) | ব্যতিক্রম হ্যান্ডল করে। |
import java.util.concurrent.CompletableFuture;
public class CompletableFutureExample {
public static void main(String[] args) {
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
System.out.println("Performing a task...");
return 42;
});
future.thenApply(result -> result * 2)
.thenAccept(result -> System.out.println("Final Result: " + result));
System.out.println("Doing other work...");
}
}
import java.util.concurrent.CompletableFuture;
public class CombineFuturesExample {
public static void main(String[] args) {
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> 10);
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> 20);
CompletableFuture<Integer> resultFuture = future1.thenCombine(future2, Integer::sum);
resultFuture.thenAccept(result -> System.out.println("Sum: " + result));
}
}
import java.util.concurrent.CompletableFuture;
public class ExceptionHandlingExample {
public static void main(String[] args) {
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
if (true) {
throw new RuntimeException("Something went wrong!");
}
return 42;
});
future.exceptionally(ex -> {
System.out.println("Exception: " + ex.getMessage());
return 0;
}).thenAccept(result -> System.out.println("Result: " + result));
}
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class FixedThreadPoolExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(3);
Runnable task = () -> {
System.out.println(Thread.currentThread().getName() + " is running.");
};
for (int i = 0; i < 5; i++) {
executor.submit(task);
}
executor.shutdown();
}
}
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ScheduledExecutorExample {
public static void main(String[] args) {
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
Runnable task = () -> System.out.println("Executing at: " + System.currentTimeMillis());
scheduler.scheduleAtFixedRate(task, 0, 2, TimeUnit.SECONDS);
}
}
exceptionally
বা handle
মেথড ব্যবহার করে ব্যতিক্রম হ্যান্ডল করুন।thenCombine
এবং thenCompose
ব্যবহার করুন।ScheduledExecutorService
ব্যবহার করুন।Future, CompletableFuture, এবং Executor কনকারেন্সি এবং অ্যাসিঙ্ক্রোনাস প্রোগ্রামিংয়ে অত্যন্ত কার্যকর। তারা সিস্টেমের পারফরম্যান্স উন্নত করে এবং জটিল অ্যাসিঙ্ক্রোনাস লজিক সহজ করে। CompletableFuture
এবং ExecutorService
একসাথে ব্যবহার করলে কোড আরও পরিষ্কার এবং কার্যকর হয়।
জাভাতে Advanced Locking Techniques এবং Custom Locks ব্যবহার করে মাল্টিথ্রেডিং প্রোগ্রামিং আরও কার্যকর এবং নমনীয় করা যায়। এটি ডেটা সিঙ্ক্রোনাইজেশন উন্নত করে এবং রেস কন্ডিশন এড়াতে সাহায্য করে।
জাভার java.util.concurrent.locks
প্যাকেজে বিভিন্ন ধরনের লকিং টেকনিক পাওয়া যায়। এগুলো মাল্টিথ্রেডেড পরিবেশে সিঙ্ক্রোনাইজেশনের উন্নত পদ্ধতি প্রদান করে।
ReentrantLock
একটি উন্নত লক, যা থ্রেডকে একই লক বারবার ধরে রাখতে দেয়, যদি এটি আগেই লক করে থাকে। এটি ঐচ্ছিক টাইমআউট এবং ইন্টারাপশন সমর্থন করে।
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExample {
private final ReentrantLock lock = new ReentrantLock();
private int counter = 0;
public void increment() {
lock.lock();
try {
counter++;
System.out.println(Thread.currentThread().getName() + " incremented counter to: " + counter);
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
ReentrantLockExample example = new ReentrantLockExample();
Runnable task = example::increment;
Thread t1 = new Thread(task, "Thread-1");
Thread t2 = new Thread(task, "Thread-2");
t1.start();
t2.start();
}
}
ReadWriteLock
একসঙ্গে একাধিক থ্রেডকে পড়ার অনুমতি দেয়, কিন্তু লেখার জন্য শুধুমাত্র এক থ্রেডকে অনুমতি দেয়। এটি রিড-হেভি অ্যাপ্লিকেশনের জন্য উপযুক্ত।
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockExample {
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private int value = 0;
public int readValue() {
lock.readLock().lock();
try {
return value;
} finally {
lock.readLock().unlock();
}
}
public void writeValue(int newValue) {
lock.writeLock().lock();
try {
value = newValue;
System.out.println("Value updated to: " + value);
} finally {
lock.writeLock().unlock();
}
}
public static void main(String[] args) {
ReadWriteLockExample example = new ReadWriteLockExample();
Thread reader = new Thread(() -> {
System.out.println("Read Value: " + example.readValue());
});
Thread writer = new Thread(() -> example.writeValue(42));
reader.start();
writer.start();
}
}
StampedLock
একটি উন্নত লকিং টেকনিক, যা optimistic locking সমর্থন করে। এটি ReadWriteLock
এর তুলনায় দ্রুততর।
import java.util.concurrent.locks.StampedLock;
public class StampedLockExample {
private final StampedLock lock = new StampedLock();
private int value = 0;
public int readValue() {
long stamp = lock.tryOptimisticRead();
int currentValue = value;
if (!lock.validate(stamp)) { // চেক করুন লক বৈধ কিনা
stamp = lock.readLock();
try {
currentValue = value;
} finally {
lock.unlockRead(stamp);
}
}
return currentValue;
}
public void writeValue(int newValue) {
long stamp = lock.writeLock();
try {
value = newValue;
System.out.println("Value updated to: " + value);
} finally {
lock.unlockWrite(stamp);
}
}
public static void main(String[] args) {
StampedLockExample example = new StampedLockExample();
Thread reader = new Thread(() -> {
System.out.println("Read Value: " + example.readValue());
});
Thread writer = new Thread(() -> example.writeValue(42));
reader.start();
writer.start();
}
}
কাস্টম লক তৈরি করতে Lock
ইন্টারফেস ব্যবহার করে নিজস্ব লকিং মেকানিজম বাস্তবায়ন করা যায়।
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.Condition;
class SimpleLock implements Lock {
private boolean isLocked = false;
@Override
public synchronized void lock() {
while (isLocked) {
try {
wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
isLocked = true;
}
@Override
public synchronized void unlock() {
isLocked = false;
notify();
}
@Override
public void lockInterruptibly() throws InterruptedException {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public boolean tryLock() {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public boolean tryLock(long time, java.util.concurrent.TimeUnit unit) throws InterruptedException {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public Condition newCondition() {
throw new UnsupportedOperationException("Not supported yet.");
}
}
public class CustomLockExample {
private final SimpleLock lock = new SimpleLock();
private int counter = 0;
public void increment() {
lock.lock();
try {
counter++;
System.out.println(Thread.currentThread().getName() + " incremented counter to: " + counter);
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
CustomLockExample example = new CustomLockExample();
Runnable task = example::increment;
Thread t1 = new Thread(task, "Thread-1");
Thread t2 = new Thread(task, "Thread-2");
t1.start();
t2.start();
}
}
class CustomReentrantLock {
private boolean isLocked = false;
private Thread lockingThread = null;
private int holdCount = 0;
public synchronized void lock() {
Thread currentThread = Thread.currentThread();
while (isLocked && currentThread != lockingThread) {
try {
wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
isLocked = true;
lockingThread = currentThread;
holdCount++;
}
public synchronized void unlock() {
if (Thread.currentThread() == lockingThread) {
holdCount--;
if (holdCount == 0) {
isLocked = false;
lockingThread = null;
notify();
}
}
}
}
ReentrantLock
, ReadWriteLock
, এবং StampedLock
উন্নত কনকারেন্সি সমাধান প্রদান করে।জাভাতে মাল্টিথ্রেডিং ও কনকারেন্সি ব্যবস্থাপনা সহজ করার জন্য কিছু ক্লাসিক্যাল Design Patterns ব্যবহার করা হয়। এর মধ্যে সবচেয়ে গুরুত্বপূর্ণ দুটি হল:
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
class Producer implements Runnable {
private BlockingQueue<Integer> queue;
public Producer(BlockingQueue<Integer> queue) {
this.queue = queue;
}
@Override
public void run() {
try {
for (int i = 1; i <= 10; i++) {
System.out.println("Produced: " + i);
queue.put(i); // কিউতে ডেটা যোগ করা
Thread.sleep(500); // ডেটা তৈরি করার সময়
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
class Consumer implements Runnable {
private BlockingQueue<Integer> queue;
public Consumer(BlockingQueue<Integer> queue) {
this.queue = queue;
}
@Override
public void run() {
try {
while (true) {
Integer data = queue.take(); // কিউ থেকে ডেটা নেওয়া
System.out.println("Consumed: " + data);
Thread.sleep(1000); // ডেটা প্রসেস করার সময়
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
public class ProducerConsumerExample {
public static void main(String[] args) {
BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(5);
Thread producerThread = new Thread(new Producer(queue));
Thread consumerThread = new Thread(new Consumer(queue));
producerThread.start();
consumerThread.start();
}
}
import java.util.concurrent.locks.ReentrantReadWriteLock;
class SharedResource {
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private int data = 0;
public void write(int value) {
lock.writeLock().lock(); // রাইট লক নিন
try {
System.out.println(Thread.currentThread().getName() + " is writing: " + value);
data = value;
Thread.sleep(1000); // লেখার সময়
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.writeLock().unlock(); // রাইট লক মুক্ত করুন
}
}
public int read() {
lock.readLock().lock(); // রিড লক নিন
try {
System.out.println(Thread.currentThread().getName() + " is reading: " + data);
Thread.sleep(500); // পড়ার সময়
return data;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return -1;
} finally {
lock.readLock().unlock(); // রিড লক মুক্ত করুন
}
}
}
public class ReadersWritersExample {
public static void main(String[] args) {
SharedResource resource = new SharedResource();
// রিডার থ্রেড
Runnable readerTask = () -> {
while (true) {
resource.read();
}
};
// রাইটার থ্রেড
Runnable writerTask = () -> {
int value = 1;
while (true) {
resource.write(value++);
}
};
// থ্রেড চালানো
Thread reader1 = new Thread(readerTask, "Reader-1");
Thread reader2 = new Thread(readerTask, "Reader-2");
Thread writer = new Thread(writerTask, "Writer");
reader1.start();
reader2.start();
writer.start();
}
}
বৈশিষ্ট্য | Producer-Consumer | Readers-Writers |
---|---|---|
ব্যবহার ক্ষেত্র | ডেটা উৎপাদন ও ব্যবস্থাপনা | পড়া ও লেখা পরিচালনা |
থ্রেড সমন্বয় | প্রযোজক এবং গ্রাহকের মধ্যে সমন্বয় | রিডার এবং রাইটারের মধ্যে সমন্বয় |
কনকারেন্সি লেভেল | প্রযোজক বা গ্রাহক একত্রে কাজ করতে পারে না | একাধিক রিডার একত্রে কাজ করতে পারে |
সুবিধা | সরল এবং থ্রেড-সেফ | ডেটার উপর নিয়ন্ত্রণ এবং সমান্তরাল পড়া |
এই প্যাটার্নগুলো সঠিকভাবে প্রয়োগ করলে মাল্টিথ্রেডেড অ্যাপ্লিকেশন আরও কার্যকর ও দক্ষ হবে।
মাল্টিথ্রেডেড প্রোগ্রামিং করতে গেলে জাভার কনকারেন্সি টুল এবং টেকনোলজি ব্যবহার অত্যন্ত গুরুত্বপূর্ণ। তবে এটি সঠিকভাবে না করলে ডেডলক, রেস কন্ডিশন, এবং পারফরম্যান্স সমস্যার মুখোমুখি হতে হয়। নিচে Best Practices এবং Common Pitfalls এর আলোচনা করা হলো।
ExecutorService
সরাসরি Thread
তৈরি এবং পরিচালনার তুলনায় অনেক কার্যকর এবং পড়ার সহজ।কোড উদাহরণ:
ExecutorService executor = Executors.newFixedThreadPool(5);
executor.submit(() -> {
System.out.println("Task executed by thread: " + Thread.currentThread().getName());
});
executor.shutdown();
synchronized
এর পরিবর্তে Lock
ব্যবহার করুনReentrantLock
উন্নত নিয়ন্ত্রণ এবং ডেডলক প্রতিরোধে সাহায্য করে।কোড উদাহরণ:
ReentrantLock lock = new ReentrantLock();
try {
lock.lock();
System.out.println("Critical section");
} finally {
lock.unlock(); // ডেডলক প্রতিরোধ
}
কোড উদাহরণ:
final class ImmutableExample {
private final int value;
public ImmutableExample(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
volatile
ব্যবহার করুন Visibility নিশ্চিত করতেvolatile
পরিবর্তনশীল ব্যবহার করলে একাধিক থ্রেডের মধ্যে ডেটা সিঙ্ক্রোনাইজ থাকে।কোড উদাহরণ:
private volatile boolean running = true;
public void stop() {
running = false;
}
ConcurrentHashMap
, CopyOnWriteArrayList
ইত্যাদি ব্যবহার করুন।কোড উদাহরণ:
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("One", 1);
System.out.println(map.get("One"));
কোড উদাহরণ:
ExecutorService executor = Executors.newCachedThreadPool();
executor.execute(() -> {
System.out.println("Task executed in thread pool");
});
executor.shutdown();
tryLock()
ব্যবহার করুন।কোড উদাহরণ:
if (lock.tryLock(10, TimeUnit.SECONDS)) {
try {
// Critical section
} finally {
lock.unlock();
}
}
AtomicInteger
, AtomicLong
ইত্যাদি ব্যবহার করুন।কোড উদাহরণ:
AtomicInteger counter = new AtomicInteger(0);
counter.incrementAndGet();
কোড উদাহরণ:
CompletableFuture.supplyAsync(() -> "Result")
.thenAccept(System.out::println);
synchronized
বা Lock
ব্যবহার করুন।Atomic
ক্লাস ব্যবহার করুন।Fair Locks ব্যবহার করুন:
ReentrantLock lock = new ReentrantLock(true); // Fair Lock
InterruptedException
সঠিকভাবে পরিচালিত না হলে থ্রেড অনির্দিষ্ট অবস্থায় থাকে।সমাধান:
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // সঠিকভাবে ইন্টারাপ্ট পরিচালনা
}
wait()
এবং notify()
সমাধান: synchronized
ব্লকের মধ্যে wait()
এবং notify()
ব্যবহার করুন।
synchronized (lock) {
lock.wait();
lock.notify();
}
সমাধান: offer()
এবং poll()
ব্যবহার করুন।
queue.offer(item, 2, TimeUnit.SECONDS);
queue.poll(2, TimeUnit.SECONDS);
Thread
তৈরি এবং পরিচালনা ব্যয়বহুল এবং জটিল।ExecutorService
ব্যবহার করুন।Concurrent
Collections ব্যবহার করুন।ExecutorService
এবং ThreadPool
ব্যবহার করুন।Atomic
ক্লাস ব্যবহার করুন।synchronized
, volatile
, এবং Lock
ব্যবহার করুন।সঠিক পদ্ধতি অনুসরণ করলে কনকারেন্সি প্রোগ্রামিং আরো নিরাপদ, কার্যকর এবং স্থিতিশীল হবে।
Read more