Advanced Concurrency Patterns এবং Best Practices

Java Technologies - জাভা কনকারেন্সি (Java Concurrency)
109
109

জাভা কনকারেন্সিতে উন্নত প্যাটার্ন ও সেরা প্র্যাকটিস ব্যবহার করে অ্যাপ্লিকেশনগুলোর পারফরম্যান্স, রিডেবিলিটি, এবং সঠিকতা উন্নত করা যায়।


Advanced Concurrency Patterns

১. Producer-Consumer Pattern

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

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 Pattern

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 Pattern

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;
    }
}

৫. Thread Local Storage Pattern

একটি থ্রেডের নিজস্ব ডেটা সংরক্ষণ করার জন্য 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();
    }
}

Best Practices for Concurrency

১. Thread Pool ব্যবহার করুন

  • সরাসরি Thread তৈরি এড়িয়ে ExecutorService ব্যবহার করুন।

২. Immutable Objects ব্যবহার করুন

  • Immutable Object ব্যবহার করলে ডেটা কনকারেন্সি সমস্যা কমে যায়।

৩. Deadlock এবং Starvation এড়িয়ে চলুন

  • লক করার অর্ডার নিশ্চিত করুন।
  • tryLock() ব্যবহার করুন।

৪. Shared Resources কম ব্যবহার করুন

  • ডেটা ভাগাভাগি এড়িয়ে ThreadLocal অথবা Immutable Objects ব্যবহার করুন।

৫. Proper Synchronization নিশ্চিত করুন

  • শুধুমাত্র প্রয়োজনীয় অংশে synchronized ব্যবহার করুন।
  • বিকল্প হিসেবে ReentrantLock এবং ReadWriteLock ব্যবহার করুন।

৬. Concurrent Collections ব্যবহার করুন

  • ConcurrentHashMap, CopyOnWriteArrayList, এবং BlockingQueue এর মতো থ্রেড-সেফ কালেকশন ব্যবহার করুন।

৭. Atomic Variables ব্যবহার করুন

  • AtomicInteger, AtomicLong, এবং AtomicReference এর মতো লক-মুক্ত ডেটা স্ট্রাকচার ব্যবহার করুন।

Performance Optimization Tips

  1. Profiling এবং Monitoring:
    • Java VisualVM বা Java Mission Control ব্যবহার করুন।
  2. Garbage Collection টিউনিং:
    • JVM ফ্ল্যাগ (যেমন, -Xms, -Xmx, -XX:+UseG1GC) ব্যবহার করুন।
  3. Avoid Over-Synchronization:
    • অতিরিক্ত synchronized ব্যবহার এড়িয়ে Lock-Free Structures ব্যবহার করুন।

জাভা কনকারেন্সির Advanced Patterns এবং Best Practices সঠিকভাবে ব্যবহার করলে:

  1. মাল্টিথ্রেডিংয়ের সমস্যাগুলো কার্যকরভাবে সমাধান করা যায়।
  2. অ্যাপ্লিকেশন পারফরম্যান্স উন্নত হয়।
  3. ডেডলক, স্টারভেশন এবং কনকারেন্সি সম্পর্কিত অন্যান্য সমস্যাগুলো সহজে প্রতিরোধ করা যায়।

সঠিক প্যাটার্ন নির্বাচন এবং সেরা প্র্যাকটিস মেনে চললে কনকারেন্ট প্রোগ্রামিং আরও নিরাপদ এবং দক্ষ হয়।

Content added By

Future, CompletableFuture এবং Executor এর উন্নত ব্যবহার

86
86

Java Concurrency API তে Future, CompletableFuture, এবং Executor মাল্টিথ্রেডেড প্রোগ্রামিং আরও সহজ ও কার্যকর করে। তারা অ্যাসিঙ্ক্রোনাস প্রোগ্রামিং এবং কনকারেন্ট টাস্ক ম্যানেজমেন্টের জন্য উন্নত সমাধান প্রদান করে।


1. Future

Future কী?

  • Future ইন্টারফেস একটি অ্যাসিঙ্ক্রোনাস টাস্কের রেজাল্ট ধরে রাখে, যা ভবিষ্যতে উপলব্ধ হবে।
  • এটি ExecutorService এর সাহায্যে ব্যবহার করা হয়।

Future এর প্রধান মেথডসমূহ:

মেথডবর্ণনা
get()রেজাল্ট ফেরত দেয়; যদি রেজাল্ট প্রস্তুত না হয়, এটি ব্লক হয়।
get(long timeout, TimeUnit unit)নির্দিষ্ট সময় পর্যন্ত অপেক্ষা করে রেজাল্ট রিটার্ন করে।
isDone()টাস্ক সম্পন্ন হয়েছে কিনা তা চেক করে।
cancel(boolean mayInterruptIfRunning)টাস্ক বাতিল করার জন্য।

Future এর উদাহরণ:

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();
    }
}

Future এর সীমাবদ্ধতা:

  1. ব্লকিং: get() মেথড ব্লক করে রাখে যতক্ষণ না রেজাল্ট প্রস্তুত।
  2. ক্যোডিনেশন কঠিন: চেইনিং এবং কমপ্লেক্স অ্যাসিঙ্ক্রোনাস লজিক পরিচালনা করা কঠিন।

2. CompletableFuture

CompletableFuture কী?

  • CompletableFuture হলো Future-এর একটি উন্নত সংস্করণ যা নন-ব্লকিং এবং চেইনড অ্যাসিঙ্ক্রোনাস টাস্ক পরিচালনা করতে সক্ষম।
  • এটি Functional Programming এবং Callback Mechanism সমর্থন করে।

CompletableFuture এর প্রধান মেথডসমূহ:

মেথডবর্ণনা
supplyAsync(Supplier<T>)অ্যাসিঙ্ক্রোনাস টাস্ক শুরু করে এবং রেজাল্ট ফেরত দেয়।
thenApply(Function<T, R>)রেজাল্টে একটি অপারেশন প্রয়োগ করে।
thenAccept(Consumer<T>)রেজাল্ট গ্রহণ করে এবং কিছু করে (রিটার্ন কিছুই নয়)।
thenCombine(CompletableFuture, BiFunction)দুইটি CompletableFuture এর রেজাল্ট একত্রিত করে।
exceptionally(Function<Throwable, T>)ব্যতিক্রম হ্যান্ডল করে।

CompletableFuture এর উদাহরণ:

১. Basic Usage

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...");
    }
}

২. Combining Multiple Futures

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));
    }
}

৩. Exception Handling

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));
    }
}

3. Executor

ExecutorService:

  • এটি Java এর থ্রেড ম্যানেজমেন্ট সহজ করে এবং টাস্কগুলিকে একটি থ্রেড পুলে জমা করার অনুমতি দেয়।

Executor এর উন্নত ব্যবহার:

১. Fixed Thread Pool

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();
    }
}

২. Scheduled Executor

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);
    }
}

Best Practices

  1. Non-blocking Code:
    • CompletableFuture ব্যবহার করে নন-ব্লকিং অপারেশন ডিজাইন করুন।
  2. Exception Handling:
    • exceptionally বা handle মেথড ব্যবহার করে ব্যতিক্রম হ্যান্ডল করুন।
  3. Thread Pool Size Optimization:
    • সঠিক Thread Pool Size কনফিগার করুন।
  4. Combine Multiple Futures:
    • একাধিক অ্যাসিঙ্ক্রোনাস টাস্ক একত্রিত করতে thenCombine এবং thenCompose ব্যবহার করুন।
  5. Scheduled Tasks:
    • নির্দিষ্ট সময়ে টাস্ক সম্পাদনের জন্য ScheduledExecutorService ব্যবহার করুন।

Future, CompletableFuture, এবং Executor কনকারেন্সি এবং অ্যাসিঙ্ক্রোনাস প্রোগ্রামিংয়ে অত্যন্ত কার্যকর। তারা সিস্টেমের পারফরম্যান্স উন্নত করে এবং জটিল অ্যাসিঙ্ক্রোনাস লজিক সহজ করে। CompletableFuture এবং ExecutorService একসাথে ব্যবহার করলে কোড আরও পরিষ্কার এবং কার্যকর হয়।

Content added By

Advanced Locking Techniques এবং Custom Locks তৈরি করা

79
79

জাভাতে Advanced Locking Techniques এবং Custom Locks ব্যবহার করে মাল্টিথ্রেডিং প্রোগ্রামিং আরও কার্যকর এবং নমনীয় করা যায়। এটি ডেটা সিঙ্ক্রোনাইজেশন উন্নত করে এবং রেস কন্ডিশন এড়াতে সাহায্য করে।


Advanced Locking Techniques

জাভার java.util.concurrent.locks প্যাকেজে বিভিন্ন ধরনের লকিং টেকনিক পাওয়া যায়। এগুলো মাল্টিথ্রেডেড পরিবেশে সিঙ্ক্রোনাইজেশনের উন্নত পদ্ধতি প্রদান করে।


১. ReentrantLock

ReentrantLock একটি উন্নত লক, যা থ্রেডকে একই লক বারবার ধরে রাখতে দেয়, যদি এটি আগেই লক করে থাকে। এটি ঐচ্ছিক টাইমআউট এবং ইন্টারাপশন সমর্থন করে।

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

ReadWriteLock একসঙ্গে একাধিক থ্রেডকে পড়ার অনুমতি দেয়, কিন্তু লেখার জন্য শুধুমাত্র এক থ্রেডকে অনুমতি দেয়। এটি রিড-হেভি অ্যাপ্লিকেশনের জন্য উপযুক্ত।

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

StampedLock একটি উন্নত লকিং টেকনিক, যা optimistic locking সমর্থন করে। এটি ReadWriteLock এর তুলনায় দ্রুততর।

StampedLock উদাহরণ:
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();
    }
}

Custom Locks তৈরি করা

কাস্টম লক তৈরি করতে Lock ইন্টারফেস ব্যবহার করে নিজস্ব লকিং মেকানিজম বাস্তবায়ন করা যায়।


Custom Lock উদাহরণ: SimpleLock

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();
    }
}

Custom ReentrantLock উদাহরণ

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();
            }
        }
    }
}

Custom Locks এর সুবিধা

  1. নিয়ন্ত্রণ বৃদ্ধি: লকিং মেকানিজমে কাস্টমাইজেশন।
  2. কনটেক্সট-স্পেসিফিক লক: নির্দিষ্ট সমস্যা সমাধানে ফোকাসড।

  • Advanced Locking Techniques যেমন ReentrantLock, ReadWriteLock, এবং StampedLock উন্নত কনকারেন্সি সমাধান প্রদান করে।
  • Custom Locks তৈরি করে নির্দিষ্ট সমস্যার জন্য বিশেষ লকিং মেকানিজম তৈরি করা যায়।
  • সঠিক লকিং প্রযুক্তি ব্যবহার করে ডেটা সিঙ্ক্রোনাইজেশন উন্নত করা এবং রেস কন্ডিশন এড়ানো সম্ভব।
Content added By

Concurrency এর জন্য Design Patterns (Producer-Consumer, Readers-Writers)

141
141

জাভাতে মাল্টিথ্রেডিং ও কনকারেন্সি ব্যবস্থাপনা সহজ করার জন্য কিছু ক্লাসিক্যাল Design Patterns ব্যবহার করা হয়। এর মধ্যে সবচেয়ে গুরুত্বপূর্ণ দুটি হল:

  1. Producer-Consumer Pattern
  2. Readers-Writers Pattern

১. Producer-Consumer Pattern

বিবরণ:

  • Producer-Consumer প্যাটার্ন একটি সাধারণ কনকারেন্সি সমস্যা যেখানে একাধিক প্রযোজক (Producer) ডেটা তৈরি করে এবং একাধিক গ্রাহক (Consumer) সেই ডেটা প্রসেস করে।
  • BlockingQueue ব্যবহার করলে এটি সহজ হয়।

সমস্যার সমাধান:

  • একটি shared buffer (queue) ব্যবহার করা হয়।
  • প্রযোজক ডেটা যোগ করে এবং গ্রাহক ডেটা তুলে নেয়।
  • যদি কিউ পূর্ণ থাকে, প্রযোজক অপেক্ষা করে।
  • যদি কিউ খালি থাকে, গ্রাহক অপেক্ষা করে।

উদাহরণ:

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();
    }
}

কী ঘটবে:

  1. প্রযোজক ডেটা তৈরি করে কিউতে যোগ করবে।
  2. গ্রাহক কিউ থেকে ডেটা তুলে নেবে।
  3. কিউ পূর্ণ হলে প্রযোজক থামবে এবং কিউ খালি হলে গ্রাহক থামবে।

২. Readers-Writers Pattern

বিবরণ:

  • Readers-Writers প্যাটার্ন এমন পরিস্থিতিতে ব্যবহৃত হয় যেখানে অনেক থ্রেড পড়তে (read) এবং কিছু থ্রেড লিখতে (write) চায়।
  • লক্ষ্য:
    1. রিডারদের মধ্যে সমান্তরাল পড়ার অনুমতি দিন।
    2. রাইটারদের একচেটিয়াভাবে (exclusive) লিখতে দিন।
    3. রিডার ও রাইটারদের মধ্যে প্রায়োরিটি সঠিকভাবে বজায় রাখুন।

উদাহরণ:

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();
    }
}

কী ঘটবে:

  1. রিডাররা একসঙ্গে পড়তে পারবে।
  2. রাইটার একা ডেটা লিখবে এবং সেই সময় অন্য রিডার বা রাইটার ঢুকতে পারবে না।

Producer-Consumer এবং Readers-Writers এর তুলনা

বৈশিষ্ট্যProducer-ConsumerReaders-Writers
ব্যবহার ক্ষেত্রডেটা উৎপাদন ও ব্যবস্থাপনাপড়া ও লেখা পরিচালনা
থ্রেড সমন্বয়প্রযোজক এবং গ্রাহকের মধ্যে সমন্বয়রিডার এবং রাইটারের মধ্যে সমন্বয়
কনকারেন্সি লেভেলপ্রযোজক বা গ্রাহক একত্রে কাজ করতে পারে নাএকাধিক রিডার একত্রে কাজ করতে পারে
সুবিধাসরল এবং থ্রেড-সেফডেটার উপর নিয়ন্ত্রণ এবং সমান্তরাল পড়া

  1. Producer-Consumer Pattern:
    • ব্যবহার করুন যখন একটি থ্রেড ডেটা তৈরি করবে এবং অন্যটি তা প্রসেস করবে।
    • BlockingQueue এর মত স্ট্রাকচার ব্যবহার করে সহজ করা যায়।
  2. Readers-Writers Pattern:
    • ব্যবহার করুন যেখানে পড়ার অপারেশন বেশি এবং লেখার অপারেশন কম।
    • ReentrantReadWriteLock এর মাধ্যমে রিডারদের সমান্তরাল পড়ার অনুমতি দিন এবং রাইটারদের একচেটিয়াভাবে লিখতে দিন।

এই প্যাটার্নগুলো সঠিকভাবে প্রয়োগ করলে মাল্টিথ্রেডেড অ্যাপ্লিকেশন আরও কার্যকর ও দক্ষ হবে।

Content added By

Concurrency Best Practices এবং Common Pitfalls

78
78

মাল্টিথ্রেডেড প্রোগ্রামিং করতে গেলে জাভার কনকারেন্সি টুল এবং টেকনোলজি ব্যবহার অত্যন্ত গুরুত্বপূর্ণ। তবে এটি সঠিকভাবে না করলে ডেডলক, রেস কন্ডিশন, এবং পারফরম্যান্স সমস্যার মুখোমুখি হতে হয়। নিচে Best Practices এবং Common Pitfalls এর আলোচনা করা হলো।


Concurrency Best Practices

১. Executor Framework ব্যবহার করুন

  • কেন ব্যবহার করবেন: 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(); // ডেডলক প্রতিরোধ
    }
    

৩. Immutable Objects ব্যবহার করুন

  • Immutable Objects থ্রেড-সেফ এবং একাধিক থ্রেডের জন্য সহজ।
  • কোড উদাহরণ:

    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;
    }
    

৫. Thread-Safe Collections ব্যবহার করুন

  • ConcurrentHashMap, CopyOnWriteArrayList ইত্যাদি ব্যবহার করুন।
  • কোড উদাহরণ:

    ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
    map.put("One", 1);
    System.out.println(map.get("One"));
    

৬. ThreadPool ব্যবহার করুন নতুন থ্রেড তৈরির পরিবর্তে

  • ThreadPool নতুন থ্রেড তৈরি এবং ব্যবস্থাপনার তুলনায় বেশি কার্যকর।
  • কোড উদাহরণ:

    ExecutorService executor = Executors.newCachedThreadPool();
    executor.execute(() -> {
        System.out.println("Task executed in thread pool");
    });
    executor.shutdown();
    

৭. Deadlock প্রতিরোধ করুন

  • কৌশল:
    • লক অর্ডার নির্ধারণ করুন।
    • টাইমআউট সহ tryLock() ব্যবহার করুন।
  • কোড উদাহরণ:

    if (lock.tryLock(10, TimeUnit.SECONDS)) {
        try {
            // Critical section
        } finally {
            lock.unlock();
        }
    }
    

৮. Atomic Classes ব্যবহার করুন

  • AtomicInteger, AtomicLong ইত্যাদি ব্যবহার করুন।
  • কোড উদাহরণ:

    AtomicInteger counter = new AtomicInteger(0);
    counter.incrementAndGet();
    

৯. CompletableFuture ব্যবহার করুন অ্যাসিনক্রোনাস টাস্কের জন্য

  • অ্যাসিনক্রোনাস প্রোগ্রামিং সহজ এবং কার্যকর করতে।
  • কোড উদাহরণ:

    CompletableFuture.supplyAsync(() -> "Result")
        .thenAccept(System.out::println);
    

Common Pitfalls (সাধারণ সমস্যাগুলো)

১. ডেডলক (Deadlock)

  • সমস্যা: দুটি বা ততোধিক থ্রেড একে অপরের লক প্রত্যাশা করে।
  • সমাধান: লকিং অর্ডার নির্ধারণ করুন এবং টাইমআউট সহ লক ব্যবহার করুন।

২. রেস কন্ডিশন (Race Condition)

  • সমস্যা: একাধিক থ্রেড একসাথে ডেটা আপডেট করে এবং ভুল ফলাফল দেয়।
  • সমাধান:
    • synchronized বা Lock ব্যবহার করুন।
    • Atomic ক্লাস ব্যবহার করুন।

৩. Resource Starvation

  • সমস্যা: একটি থ্রেড সবসময় রিসোর্স পায় এবং অন্য থ্রেড অপেক্ষায় থাকে।
  • সমাধান:
    • Fair Locks ব্যবহার করুন:

      ReentrantLock lock = new ReentrantLock(true); // Fair Lock
      

৪. Incorrect Thread Interruption Handling

  • সমস্যা: InterruptedException সঠিকভাবে পরিচালিত না হলে থ্রেড অনির্দিষ্ট অবস্থায় থাকে।
  • সমাধান:

    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt(); // সঠিকভাবে ইন্টারাপ্ট পরিচালনা
    }
    

৫. Improper Use of wait() এবং notify()

  • সমস্যা: সঠিকভাবে লক ব্যবহার না করলে রেস কন্ডিশন হয়।
  • সমাধান: synchronized ব্লকের মধ্যে wait() এবং notify() ব্যবহার করুন।

    synchronized (lock) {
        lock.wait();
        lock.notify();
    }
    

৬. BlockingQueue-এ Deadlock

  • সমস্যা: প্রযোজক-গ্রাহক মডেলে ভুল পরিচালনা ডেডলক সৃষ্টি করতে পারে।
  • সমাধান: offer() এবং poll() ব্যবহার করুন।

    queue.offer(item, 2, TimeUnit.SECONDS);
    queue.poll(2, TimeUnit.SECONDS);
    

৭. Manual Thread Management

  • সমস্যা: সরাসরি Thread তৈরি এবং পরিচালনা ব্যয়বহুল এবং জটিল।
  • সমাধান: ExecutorService ব্যবহার করুন।

৮. Improper Synchronization

  • সমস্যা: শেয়ার করা ডেটা সঠিকভাবে সিঙ্ক্রোনাইজ না করা।
  • সমাধান:
    • Immutable Objects ব্যবহার করুন।
    • Concurrent Collections ব্যবহার করুন।

Concurrency Best Practices

  1. ExecutorService এবং ThreadPool ব্যবহার করুন।
  2. Immutable Objects এবং Atomic ক্লাস ব্যবহার করুন।
  3. Deadlock এবং Race Condition প্রতিরোধ করুন।
  4. Thread-Safe Collections ব্যবহার করুন।

Avoid Common Pitfalls

  1. ডেডলক, রেস কন্ডিশন, এবং রিসোর্স স্টারভেশন প্রতিরোধ করুন।
  2. সঠিকভাবে synchronized, volatile, এবং Lock ব্যবহার করুন।
  3. থ্রেড ইন্টারাপ্ট এবং টাইমআউট সঠিকভাবে পরিচালনা করুন।

সঠিক পদ্ধতি অনুসরণ করলে কনকারেন্সি প্রোগ্রামিং আরো নিরাপদ, কার্যকর এবং স্থিতিশীল হবে।

Content added By
Promotion