Semaphore এবং CountDownLatch

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

384

জাভাতে Semaphore এবং CountDownLatch হল দুটি গুরুত্বপূর্ণ কনকারেন্সি ইউটিলিটি, যা থ্রেড সিঙ্ক্রোনাইজেশন এবং থ্রেড ব্যবস্থাপনার জন্য ব্যবহৃত হয়।


১. Semaphore

কী?

Semaphore হল একটি কনকারেন্সি কন্ট্রোল মেকানিজম, যা একাধিক থ্রেডের মধ্যে একটি নির্দিষ্ট সংখ্যক রিসোর্স শেয়ার করতে দেয়। এটি একটি কাউন্টার ব্যবহার করে রিসোর্সের অ্যাক্সেস সীমিত করে।

ব্যবহার:

  • একাধিক থ্রেডের মধ্যে সীমিত রিসোর্স শেয়ার করার জন্য।
  • থ্রেডগুলোর মাধ্যমে পারমিট নিয়ন্ত্রণ করতে।

Semaphore এর মেথড

  • acquire(): পারমিট পাওয়ার জন্য থ্রেডকে ব্লক করে।
  • release(): একটি পারমিট মুক্ত করে।
  • availablePermits(): বর্তমানে কতটি পারমিট পাওয়া যায় তা দেখায়।

Semaphore উদাহরণ

import java.util.concurrent.Semaphore;

public class SemaphoreExample {
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(2); // ২ পারমিট

        Runnable task = () -> {
            try {
                System.out.println(Thread.currentThread().getName() + " trying to acquire permit...");
                semaphore.acquire(); // পারমিট অধিকার
                System.out.println(Thread.currentThread().getName() + " acquired permit.");
                Thread.sleep(2000); // কাজ করছে
                System.out.println(Thread.currentThread().getName() + " releasing permit.");
                semaphore.release(); // পারমিট মুক্ত
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };

        Thread t1 = new Thread(task, "Thread-1");
        Thread t2 = new Thread(task, "Thread-2");
        Thread t3 = new Thread(task, "Thread-3");

        t1.start();
        t2.start();
        t3.start();
    }
}

আউটপুট (সম্ভাব্য)

Thread-1 trying to acquire permit...
Thread-1 acquired permit.
Thread-2 trying to acquire permit...
Thread-2 acquired permit.
Thread-3 trying to acquire permit...
Thread-1 releasing permit.
Thread-3 acquired permit.
Thread-2 releasing permit.
Thread-3 releasing permit.

২. CountDownLatch

কী?

CountDownLatch হল একটি সিঙ্ক্রোনাইজেশন ইউটিলিটি, যা একটি নির্দিষ্ট সংখ্যক ইভেন্ট সম্পন্ন হওয়ার পর থ্রেডকে কাজ করতে দেয়।

ব্যবহার:

  • নির্দিষ্ট সংখ্যক কাজ সম্পন্ন হওয়া পর্যন্ত থ্রেডকে ব্লক করতে।
  • থ্রেড এক্সিকিউশন কন্ট্রোল করতে।

CountDownLatch এর মেথড

  • countDown(): কাউন্টার মান ১ কমায়।
  • await(): কাউন্টার মান শূন্য হওয়া পর্যন্ত অপেক্ষা করে।

CountDownLatch উদাহরণ

import java.util.concurrent.CountDownLatch;

public class CountDownLatchExample {
    public static void main(String[] args) {
        CountDownLatch latch = new CountDownLatch(3); // ৩ ইভেন্টের জন্য অপেক্ষা

        Runnable task = () -> {
            try {
                System.out.println(Thread.currentThread().getName() + " performing task...");
                Thread.sleep(1000); // কাজ করছে
                System.out.println(Thread.currentThread().getName() + " task completed.");
                latch.countDown(); // কাউন্টার কমানো
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };

        Thread t1 = new Thread(task, "Thread-1");
        Thread t2 = new Thread(task, "Thread-2");
        Thread t3 = new Thread(task, "Thread-3");

        t1.start();
        t2.start();
        t3.start();

        try {
            latch.await(); // অপেক্ষা করা ৩ কাজ শেষ হওয়া পর্যন্ত
            System.out.println("All tasks completed. Main thread continues...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

আউটপুট (সম্ভাব্য)

Thread-1 performing task...
Thread-2 performing task...
Thread-3 performing task...
Thread-1 task completed.
Thread-2 task completed.
Thread-3 task completed.
All tasks completed. Main thread continues...

Semaphore এবং CountDownLatch এর তুলনা

বৈশিষ্ট্যSemaphoreCountDownLatch
মূল উদ্দেশ্যপারমিট ব্যবস্থাপনা এবং রিসোর্স শেয়ার করানির্দিষ্ট সংখ্যক ইভেন্ট সম্পন্ন হওয়া পর্যন্ত অপেক্ষা
কাউন্টার পরিবর্তনপারমিট বৃদ্ধি/হ্রাস করা যায়কাউন্টার শূন্য হলে থ্রেড কাজ শুরু করে
পুনঃব্যবহারযোগ্যতাপুনঃব্যবহারযোগ্যএকবার ব্যবহারের পরে পুনরায় সেট করা যায় না
অ্যাপ্লিকেশন ক্ষেত্ররিসোর্স সীমাবদ্ধতানির্দিষ্ট সংখ্যক কাজ সম্পন্ন করা

উভয় একসাথে ব্যবহার: উদাহরণ

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;

public class CombinedExample {
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(2); // ২ পারমিট
        CountDownLatch latch = new CountDownLatch(3); // ৩ কাজের জন্য অপেক্ষা

        Runnable task = () -> {
            try {
                semaphore.acquire(); // পারমিট গ্রহণ
                System.out.println(Thread.currentThread().getName() + " acquired permit. Working...");
                Thread.sleep(1000); // কাজ করছে
                System.out.println(Thread.currentThread().getName() + " releasing permit.");
                semaphore.release(); // পারমিট মুক্ত
                latch.countDown(); // কাজ সম্পন্ন হওয়ার সিগন্যাল
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };

        Thread t1 = new Thread(task, "Thread-1");
        Thread t2 = new Thread(task, "Thread-2");
        Thread t3 = new Thread(task, "Thread-3");

        t1.start();
        t2.start();
        t3.start();

        try {
            latch.await(); // কাজ শেষ হওয়া পর্যন্ত অপেক্ষা
            System.out.println("All tasks completed. Main thread proceeds...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

আউটপুট (সম্ভাব্য)

Thread-1 acquired permit. Working...
Thread-2 acquired permit. Working...
Thread-1 releasing permit.
Thread-3 acquired permit. Working...
Thread-2 releasing permit.
Thread-3 releasing permit.
All tasks completed. Main thread proceeds...

  1. Semaphore: থ্রেডগুলোর মধ্যে রিসোর্স ব্যবস্থাপনার জন্য।
  2. CountDownLatch: নির্দিষ্ট সংখ্যক কাজ সম্পন্ন হওয়া পর্যন্ত অপেক্ষা করতে।
  3. সঠিকভাবে ব্যবহারের মাধ্যমে থ্রেড সিঙ্ক্রোনাইজেশন এবং কনকারেন্সি সহজ করা যায়।
Content added By

Semaphore হলো Java Concurrency API এর একটি অংশ যা java.util.concurrent প্যাকেজে অন্তর্ভুক্ত। এটি একটি সিঙ্ক্রোনাইজেশন টুল যা অ্যাক্সেস কন্ট্রোল মেকানিজম হিসেবে কাজ করে। Semaphore একটি নির্দিষ্ট সংখ্যক থ্রেডকে একত্রে একটি রিসোর্সে প্রবেশ করতে অনুমতি দেয়।


Semaphore কীভাবে কাজ করে?

  • Permit Count:
    • Semaphore এর একটি নির্দিষ্ট permit count থাকে, যা একটি থ্রেডকে রিসোর্সে অ্যাক্সেস করার অনুমতি দেয়।
    • একটি থ্রেড যখন রিসোর্স ব্যবহার করে, তখন এটি একটি permit গ্রহণ করে। কাজ শেষ হলে permit মুক্ত করে।
  • Blocking Behavior:
    • যদি কোনো থ্রেড একটি permit পেতে ব্যর্থ হয় (কারণ সমস্ত permit ব্যস্ত), তবে সেটি ব্লক হয়ে অপেক্ষা করে যতক্ষণ না একটি permit পাওয়া যায়।
  • Concurrency Management:
    • Semaphore ব্যবহার করে নির্দিষ্ট সংখ্যক থ্রেড বা প্রক্রিয়াকে একত্রে একটি শেয়ারড রিসোর্সে কাজ করতে দেওয়া যায়।

Semaphore এর দুটি প্রধান ধরন:

  1. Counting Semaphore:
    • একটি নির্দিষ্ট সংখ্যক permit নির্ধারণ করা হয়।
    • উদাহরণ: রিসোর্সে ৫টি permit থাকলে, একবারে ৫টি থ্রেড রিসোর্সে অ্যাক্সেস করতে পারে।
  2. Binary Semaphore (Mutex):
    • এটি মূলত একটি বিশেষ ধরনের Counting Semaphore, যেখানে permit সংখ্যা ১।
    • উদাহরণ: একটি মিউটেক্স লক, যা একবারে শুধুমাত্র একটি থ্রেডকে অ্যাক্সেস করতে দেয়।

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

মেথডবর্ণনা
acquire()একটি permit গ্রহণ করে। যদি permit না থাকে, থ্রেড ব্লক হয়ে অপেক্ষা করে।
release()permit মুক্ত করে, যা অন্য থ্রেড ব্যবহার করতে পারে।
tryAcquire()একটি permit পেতে চেষ্টা করে। সফল হলে true, ব্যর্থ হলে false রিটার্ন করে।
availablePermits()বর্তমানে কতটি permit অবশিষ্ট আছে তা জানায়।

Semaphore এর উদাহরণ:

১. Counting Semaphore:

import java.util.concurrent.Semaphore;

public class SemaphoreExample {
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(3); // 3 permits available

        Runnable task = () -> {
            try {
                System.out.println(Thread.currentThread().getName() + " is trying to acquire permit...");
                semaphore.acquire(); // Acquire a permit
                System.out.println(Thread.currentThread().getName() + " acquired a permit.");
                
                // Simulate some work
                Thread.sleep(2000);
                
                System.out.println(Thread.currentThread().getName() + " is releasing permit...");
                semaphore.release(); // Release the permit
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };

        // Create and start multiple threads
        for (int i = 1; i <= 5; i++) {
            new Thread(task, "Thread-" + i).start();
        }
    }
}

আউটপুট (উদাহরণস্বরূপ):

Thread-1 is trying to acquire permit...
Thread-2 is trying to acquire permit...
Thread-3 is trying to acquire permit...
Thread-1 acquired a permit.
Thread-2 acquired a permit.
Thread-3 acquired a permit.
Thread-4 is trying to acquire permit...
Thread-5 is trying to acquire permit...
Thread-1 is releasing permit...
Thread-4 acquired a permit.

বর্ণনা:

  • এখানে একটি Semaphore তৈরি করা হয়েছে যার ৩টি permit আছে।
  • একবারে ৩টি থ্রেড permit পেয়ে কাজ শুরু করে। অতিরিক্ত থ্রেড ব্লক হয়ে অপেক্ষা করে।
  • কাজ শেষ হলে release() কল করার মাধ্যমে permit মুক্ত করা হয়।

২. Binary Semaphore (Mutex):

import java.util.concurrent.Semaphore;

public class MutexExample {
    public static void main(String[] args) {
        Semaphore mutex = new Semaphore(1); // Only 1 permit, acting as a Mutex

        Runnable criticalTask = () -> {
            try {
                System.out.println(Thread.currentThread().getName() + " is trying to acquire lock...");
                mutex.acquire();
                System.out.println(Thread.currentThread().getName() + " acquired lock.");

                // Critical section
                Thread.sleep(2000);

                System.out.println(Thread.currentThread().getName() + " is releasing lock...");
                mutex.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };

        // Create and start multiple threads
        for (int i = 1; i <= 3; i++) {
            new Thread(criticalTask, "Thread-" + i).start();
        }
    }
}

আউটপুট (উদাহরণস্বরূপ):

Thread-1 is trying to acquire lock...
Thread-1 acquired lock.
Thread-2 is trying to acquire lock...
Thread-3 is trying to acquire lock...
Thread-1 is releasing lock...
Thread-2 acquired lock.

বর্ণনা:

  • এখানে একটি Semaphore ব্যবহার করা হয়েছে মিউটেক্স লক হিসেবে।
  • একবারে শুধুমাত্র একটি থ্রেড রিসোর্স ব্যবহার করতে পারে। অন্য থ্রেডগুলো লক মুক্ত হওয়া পর্যন্ত অপেক্ষা করে।

Semaphore এর বাস্তব জীবনের প্রয়োগ:

  1. Database Connection Pooling:
    • একবারে নির্দিষ্ট সংখ্যক ডেটাবেস কানেকশন ব্যবহারের অনুমতি।
    • উদাহরণ: ১০টি কানেকশন পর্যন্ত ব্যবহার করা যায়, অতিরিক্ত থ্রেড অপেক্ষা করবে।
  2. Rate Limiting:
    • নির্দিষ্ট সংখ্যক অনুরোধকে একত্রে প্রক্রিয়াকরণ।
    • উদাহরণ: API কলের সীমাবদ্ধতা।
  3. Resource Management:
    • থ্রেড-সেফ উপায়ে সীমিত রিসোর্স ব্যবহারের নিয়ন্ত্রণ।
  4. Thread Synchronization:
    • থ্রেডের মধ্যে সিঙ্ক্রোনাইজেশন এবং ক্রিটিক্যাল সেকশন সুরক্ষিত রাখা।

Semaphore একটি শক্তিশালী সিঙ্ক্রোনাইজেশন টুল যা Java Concurrency API তে রিসোর্স শেয়ারিং এবং থ্রেড কন্ট্রোল পরিচালনার জন্য ব্যবহৃত হয়। এটি Counting এবং Binary Semaphore হিসেবে ব্যবহার করা যায়। Semaphore এর সঠিক ব্যবহার মাল্টিথ্রেডিং প্রোগ্রামে রিসোর্সের কার্যকর ব্যবস্থাপনা এবং ডেটা কনসিস্টেন্সি নিশ্চিত করতে সাহায্য করে।

Content added By

CountDownLatch জাভা কনকারেন্সি ফ্রেমওয়ার্কের একটি টুল, যা একাধিক থ্রেডের মধ্যে Coordination এবং Synchronization নিশ্চিত করার জন্য ব্যবহৃত হয়। এটি একটি কাউন্টার-ভিত্তিক মেকানিজম, যেখানে থ্রেডগুলি নির্দিষ্ট সংখ্যক কার্যক্রম সম্পন্ন হওয়া পর্যন্ত অপেক্ষা করে।


CountDownLatch কী?

  • CountDownLatch হলো java.util.concurrent প্যাকেজের একটি ক্লাস।
  • এটি একটি কাউন্টার দিয়ে কাজ করে। কাউন্টারটি একটি নির্দিষ্ট মান দিয়ে শুরু হয় এবং প্রতি countDown() কলের মাধ্যমে কমে।
  • যখন কাউন্টার শূন্যে পৌঁছায়, তখন অপেক্ষমাণ থ্রেডগুলি কাজ শুরু করতে পারে।

CountDownLatch এর ব্যবহার

  1. মাল্টিপল থ্রেড একসঙ্গে কাজ শুরু করার আগে নির্দিষ্ট শর্ত পূরণের জন্য অপেক্ষা করা।
  2. প্রধান থ্রেডকে অন্যান্য থ্রেডের কাজ শেষ হওয়া পর্যন্ত অপেক্ষা করানো।

ক্লাসের প্রধান মেথড

  1. CountDownLatch(int count): নির্দিষ্ট কাউন্টার দিয়ে একটি CountDownLatch তৈরি করে।
  2. void countDown(): কাউন্টার ১ করে কমায়।
  3. void await(): কাউন্টার শূন্য না হওয়া পর্যন্ত থ্রেডকে অপেক্ষা করায়।

CountDownLatch উদাহরণ: Multiple Thread Coordination

১. একাধিক থ্রেড কাজ শুরু করার আগে অপেক্ষা করা

import java.util.concurrent.CountDownLatch;

public class CountDownLatchExample {
    public static void main(String[] args) {
        int numberOfWorkers = 3;
        CountDownLatch latch = new CountDownLatch(1); // একবার কাউন্টডাউন করলে থ্রেডগুলো শুরু হবে

        // কাজের থ্রেড তৈরি
        for (int i = 1; i <= numberOfWorkers; i++) {
            new Thread(new Worker(latch), "Worker-" + i).start();
        }

        System.out.println("Main thread is preparing the workers...");

        try {
            Thread.sleep(2000); // মেইন থ্রেড কিছু প্রস্তুতি নিচ্ছে
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Main thread is starting the workers.");
        latch.countDown(); // সব থ্রেড কাজ শুরু করতে পারে
    }
}

class Worker implements Runnable {
    private final CountDownLatch latch;

    public Worker(CountDownLatch latch) {
        this.latch = latch;
    }

    @Override
    public void run() {
        try {
            latch.await(); // অপেক্ষা করুন যতক্ষণ না কাউন্টডাউন হয়
            System.out.println(Thread.currentThread().getName() + " is working.");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

আউটপুট:

Main thread is preparing the workers...
Main thread is starting the workers.
Worker-1 is working.
Worker-2 is working.
Worker-3 is working.

২. প্রধান থ্রেডকে অপেক্ষা করানো যতক্ষণ না কাজ শেষ হয়

import java.util.concurrent.CountDownLatch;

public class CountDownLatchCompletionExample {
    public static void main(String[] args) {
        int numberOfTasks = 3;
        CountDownLatch latch = new CountDownLatch(numberOfTasks);

        // কাজের থ্রেড তৈরি
        for (int i = 1; i <= numberOfTasks; i++) {
            new Thread(new Task(latch), "Task-" + i).start();
        }

        try {
            latch.await(); // মেইন থ্রেড অপেক্ষা করবে যতক্ষণ না কাজ শেষ হয়
            System.out.println("All tasks are completed. Main thread can proceed.");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class Task implements Runnable {
    private final CountDownLatch latch;

    public Task(CountDownLatch latch) {
        this.latch = latch;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " is starting.");
        try {
            Thread.sleep(1000); // কাজ করছে
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " has finished.");
        latch.countDown(); // কাজ শেষ হয়েছে, কাউন্টডাউন করুন
    }
}

আউটপুট:

Task-1 is starting.
Task-2 is starting.
Task-3 is starting.
Task-1 has finished.
Task-2 has finished.
Task-3 has finished.
All tasks are completed. Main thread can proceed.

CountDownLatch ব্যবহার: প্রযোজক-ভোক্তা (Producer-Consumer) উদাহরণ

import java.util.concurrent.CountDownLatch;

public class ProducerConsumerExample {
    public static void main(String[] args) {
        CountDownLatch latch = new CountDownLatch(1);

        Thread producer = new Thread(() -> {
            try {
                System.out.println("Producer is producing items...");
                Thread.sleep(2000); // আইটেম প্রোডিউস হচ্ছে
                System.out.println("Producer has finished producing items.");
                latch.countDown(); // প্রোডিউসার কাজ শেষ করেছে
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        Thread consumer = new Thread(() -> {
            try {
                System.out.println("Consumer is waiting for items...");
                latch.await(); // কনজিউমার অপেক্ষা করছে
                System.out.println("Consumer has started consuming items.");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        producer.start();
        consumer.start();
    }
}

আউটপুট:

Consumer is waiting for items...
Producer is producing items...
Producer has finished producing items.
Consumer has started consuming items.

CountDownLatch এর সুবিধা

  1. Multiple Thread Coordination: একাধিক থ্রেডের মধ্যে কার্যকর সমন্বয় নিশ্চিত করে।
  2. Concurrency Management: মেইন থ্রেড বা অন্য থ্রেডগুলোকে কাজ শেষ হওয়া পর্যন্ত অপেক্ষা করানো সহজ হয়।
  3. সিম্পল ডিজাইন: সরল API ব্যবহার করে শক্তিশালী কনকারেন্সি সমাধান।

CountDownLatch এর সীমাবদ্ধতা

  1. One-Time Use: একবার কাউন্ট শূন্যে পৌঁছে গেলে, এটি পুনরায় ব্যবহার করা যায় না। নতুন কাউন্টার দিয়ে নতুন CountDownLatch তৈরি করতে হবে।
  2. ডেডলক সম্ভাবনা: যদি কোনো থ্রেড ভুলভাবে countDown() না করে, তবে সিস্টেম ডেডলকে যেতে পারে।

CountDownLatch একটি শক্তিশালী কনকারেন্সি টুল, যা মাল্টিপল থ্রেড কোঅর্ডিনেশনে সহজ এবং কার্যকর সমাধান প্রদান করে। এটি বিশেষত এমন পরিস্থিতিতে উপযোগী যেখানে নির্দিষ্ট কাজ শেষ না হওয়া পর্যন্ত অন্য কাজ শুরু করা যাবে না। সঠিক ব্যবহার নিশ্চিত করতে কোডের লজিক পরিষ্কার রাখা গুরুত্বপূর্ণ।

Content added By

জাভার CyclicBarrier এবং Phaser হলো কনকারেন্সি টুলস, যা একাধিক থ্রেডের মধ্যে সিঙ্ক্রোনাইজেশন নিশ্চিত করতে ব্যবহৃত হয়। এগুলো java.util.concurrent প্যাকেজের অংশ এবং মাল্টিথ্রেডেড অ্যাপ্লিকেশনে ব্যবহৃত হয়।


১. CyclicBarrier

CyclicBarrier একটি ব্যারিয়ার বা প্রতিবন্ধক যা একটি নির্দিষ্ট সংখ্যক থ্রেড পৌঁছানোর পর কাজ শুরু করতে দেয়। এটি মূলত এমন পরিস্থিতিতে ব্যবহৃত হয় যেখানে নির্দিষ্ট সংখ্যক থ্রেড একসঙ্গে কাজ শুরু করবে।

বৈশিষ্ট্য:

  1. একটি নির্দিষ্ট সংখ্যক থ্রেড পৌঁছানোর অপেক্ষা করে।
  2. একবার সক্রিয় হলে এটি পুনরায় ব্যবহারযোগ্য (Cyclic)।
  3. ঐচ্ছিকভাবে একটি Runnable প্রদান করা যায়, যা ব্যারিয়ারে পৌঁছানোর পরে চালানো হয়।

CyclicBarrier উদাহরণ:

import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierExample {
    public static void main(String[] args) {
        // ৩টি থ্রেডের জন্য একটি ব্যারিয়ার তৈরি করুন
        CyclicBarrier barrier = new CyclicBarrier(3, () -> {
            System.out.println("All threads reached the barrier. Task can proceed.");
        });

        Runnable task = () -> {
            try {
                System.out.println(Thread.currentThread().getName() + " is waiting at the barrier.");
                barrier.await(); // ব্যারিয়ারে অপেক্ষা
                System.out.println(Thread.currentThread().getName() + " has crossed the barrier.");
            } catch (Exception e) {
                e.printStackTrace();
            }
        };

        // ৩টি থ্রেড চালান
        Thread t1 = new Thread(task);
        Thread t2 = new Thread(task);
        Thread t3 = new Thread(task);

        t1.start();
        t2.start();
        t3.start();
    }
}

আউটপুট:

  1. তিনটি থ্রেড ব্যারিয়ারে পৌঁছায়।
  2. ব্যারিয়ার অ্যাকশন (Runnable) চালানো হয়।
  3. থ্রেডগুলো ব্যারিয়ার পার হয়।

২. Phaser

Phaser হলো একটি নমনীয় এবং ডাইনামিক সিঙ্ক্রোনাইজেশন টুল। এটি CyclicBarrier এবং CountDownLatch এর চেয়ে বেশি কার্যকর, কারণ এটি ডাইনামিক পার্টিসিপেশন সমর্থন করে।

বৈশিষ্ট্য:

  1. একাধিক পর্যায় (Phase) পরিচালনা করতে পারে।
  2. ডাইনামিকভাবে থ্রেড যোগ এবং অপসারণ করা যায়।
  3. প্রতিটি পর্যায় সম্পন্ন হওয়ার পরে পরবর্তী পর্যায়ে যেতে পারে।

Phaser উদাহরণ:

import java.util.concurrent.Phaser;

public class PhaserExample {
    public static void main(String[] args) {
        Phaser phaser = new Phaser(1); // মূল থ্রেডসহ ১টি রেজিস্টার্ড পার্টি

        Runnable task = (phase) -> {
            int currentPhase = phaser.getPhase();
            System.out.println(Thread.currentThread().getName() + " is in phase " + currentPhase);
            phaser.arriveAndAwaitAdvance(); // পর্যায় শেষ করুন এবং অপেক্ষা করুন
        };

        // নতুন থ্রেড তৈরি এবং রেজিস্টার
        Thread t1 = new Thread(() -> {
            phaser.register(); // নতুন পার্টি রেজিস্টার
            task.run();
            task.run();
        }, "Thread-1");

        Thread t2 = new Thread(() -> {
            phaser.register();
            task.run();
            task.run();
        }, "Thread-2");

        t1.start();
        t2.start();

        // মূল থ্রেডে কাজ চালান
        for (int i = 0; i < 2; i++) {
            System.out.println("Main thread is in phase " + phaser.getPhase());
            phaser.arriveAndAwaitAdvance(); // পর্যায় শেষ করুন এবং অপেক্ষা করুন
        }

        phaser.arriveAndDeregister(); // মূল থ্রেড অপসারণ
    }
}

আউটপুট:

  • প্রতিটি থ্রেড প্রতিটি পর্যায় সম্পন্ন করে পরবর্তী পর্যায়ে যায়।
  • মূল থ্রেড ফাইনাল পর্যায়ে অপসারণ করে।

CyclicBarrier এবং Phaser এর তুলনা

বৈশিষ্ট্যCyclicBarrierPhaser
পর্যায় (Phase)একক পর্যায় পরিচালনা করতে পারেএকাধিক পর্যায় পরিচালনা করতে পারে
ডাইনামিক থ্রেড যোগডাইনামিক থ্রেড যোগ করা যায় নাথ্রেড ডাইনামিকভাবে যোগ/অপসারণ করা যায়
ব্যবহারনির্দিষ্ট সংখ্যক থ্রেডের জন্য অপেক্ষা করতেফ্লেক্সিবল এবং জটিল সিঙ্ক্রোনাইজেশনের জন্য

ব্যবহার ক্ষেত্র

  1. CyclicBarrier:
    • একাধিক থ্রেড একত্রে কাজ শুরু করতে হবে।
    • নির্দিষ্ট কাজের জন্য সীমিত সংখ্যক থ্রেড।
  2. Phaser:
    • ডাইনামিক এবং জটিল সিঙ্ক্রোনাইজেশন প্রয়োজন।
    • পর্যায়ভিত্তিক কাজ সম্পন্ন করা।

  • CyclicBarrier সহজ এবং সুনির্দিষ্ট কাজের জন্য উপযুক্ত যেখানে থ্রেডের সংখ্যা স্থির।
  • Phaser আরো নমনীয় এবং পর্যায়ভিত্তিক বা ডাইনামিক থ্রেড ব্যবস্থাপনার জন্য কার্যকর।

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

Content added By

Semaphore হলো জাভা কনকারেন্সি প্যাকেজের (java.util.concurrent.Semaphore) একটি টুল, যা থ্রেড সিঙ্ক্রোনাইজেশনের জন্য ব্যবহৃত হয়। এটি একটি নির্দিষ্ট সংখ্যক থ্রেডকে একসাথে কোনো রিসোর্স অ্যাক্সেস করার অনুমতি দেয়।


Semaphore কীভাবে কাজ করে?

  1. পারমিট (Permits): Semaphore-এ একটি নির্দিষ্ট সংখ্যক পারমিট থাকে। প্রতিটি থ্রেড একটি পারমিট গ্রহণ করে কাজ শুরু করে এবং কাজ শেষে পারমিট মুক্ত করে।
  2. Acquire & Release: থ্রেড একটি পারমিট পাওয়ার জন্য acquire() কল করে এবং কাজ শেষে release() কল করে।

Semaphore এর বাস্তব ব্যবহার

১. থ্রেড সিঙ্ক্রোনাইজেশন:

উদাহরণ: একটি রিসোর্সে সর্বাধিক ২টি থ্রেড কাজ করতে পারবে।

import java.util.concurrent.Semaphore;

public class SemaphoreExample {
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(2); // সর্বাধিক ২টি পারমিট

        Runnable task = () -> {
            try {
                System.out.println(Thread.currentThread().getName() + " is waiting for a permit...");
                semaphore.acquire(); // পারমিট গ্রহণ
                System.out.println(Thread.currentThread().getName() + " acquired a permit!");

                // রিসোর্স ব্যবহার
                Thread.sleep(2000);

                System.out.println(Thread.currentThread().getName() + " released the permit.");
                semaphore.release(); // পারমিট মুক্ত
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };

        // ৫টি থ্রেড চালু করা
        for (int i = 0; i < 5; i++) {
            new Thread(task).start();
        }
    }
}

আউটপুট:

  • সর্বোচ্চ ২টি থ্রেড একসাথে কাজ করবে।
  • বাকিরা পারমিটের জন্য অপেক্ষা করবে।

২. ডেটাবেস কানেকশন পুল ম্যানেজমেন্ট:

উদাহরণ: একটি ডেটাবেস কানেকশন পুলে সর্বাধিক ৩টি কানেকশন অ্যাক্সেস করা যাবে।

import java.util.concurrent.Semaphore;

class DatabaseConnection {
    private static final Semaphore semaphore = new Semaphore(3); // সর্বাধিক ৩টি কানেকশন

    public void accessDatabase() {
        try {
            semaphore.acquire();
            System.out.println(Thread.currentThread().getName() + " connected to the database.");

            // ডেটাবেসে কাজ করা
            Thread.sleep(2000);

            System.out.println(Thread.currentThread().getName() + " disconnected from the database.");
            semaphore.release();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class DatabaseConnectionExample {
    public static void main(String[] args) {
        DatabaseConnection connection = new DatabaseConnection();

        Runnable task = connection::accessDatabase;

        for (int i = 0; i < 10; i++) {
            new Thread(task).start();
        }
    }
}

৩. প্রযোজক-গ্রাহক সমস্যা (Producer-Consumer Problem):

উদাহরণ: একটি Buffer যেখানে সর্বাধিক ২টি আইটেম রাখা যাবে।

import java.util.concurrent.Semaphore;
import java.util.LinkedList;
import java.util.Queue;

class BoundedBuffer {
    private final Queue<Integer> buffer = new LinkedList<>();
    private final int capacity = 2;

    private final Semaphore items = new Semaphore(0); // ভোগ করার জন্য আইটেমের সংখ্যা
    private final Semaphore spaces = new Semaphore(capacity); // খালি জায়গার সংখ্যা

    public void produce(int value) throws InterruptedException {
        spaces.acquire(); // খালি জায়গা নিশ্চিত করা
        synchronized (this) {
            buffer.add(value);
            System.out.println("Produced: " + value);
        }
        items.release(); // নতুন আইটেম যোগ করা
    }

    public int consume() throws InterruptedException {
        items.acquire(); // ভোগ করার জন্য আইটেম নিশ্চিত করা
        synchronized (this) {
            int value = buffer.poll();
            System.out.println("Consumed: " + value);
            spaces.release(); // জায়গা মুক্ত করা
            return value;
        }
    }
}

public class ProducerConsumerExample {
    public static void main(String[] args) {
        BoundedBuffer buffer = new BoundedBuffer();

        Runnable producer = () -> {
            for (int i = 0; i < 5; i++) {
                try {
                    buffer.produce(i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        Runnable consumer = () -> {
            for (int i = 0; i < 5; i++) {
                try {
                    buffer.consume();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        new Thread(producer).start();
        new Thread(consumer).start();
    }
}

Semaphore এর পদ্ধতি

মেথডকাজ
acquire()একটি পারমিট গ্রহণ করে (অথবা ব্লক করে যদি পারমিট পাওয়া না যায়)।
release()একটি পারমিট মুক্ত করে।
tryAcquire()একটি পারমিট পাওয়ার চেষ্টা করে (ব্লক করে না)।
availablePermits()বর্তমানে অবশিষ্ট পারমিট সংখ্যা দেয়।

Semaphore ব্যবহার করার সময় সতর্কতা

  1. ডেডলক এড়িয়ে চলুন: acquire() কল করার পর release() নিশ্চিত করুন।
  2. পারফরম্যান্স: সীমিত সংখ্যক পারমিট ব্যবহার করুন যাতে থ্রেডগুলি খুব বেশি সময় ধরে ব্লক না থাকে।
  3. সিঙ্ক্রোনাইজেশন: যদি একই রিসোর্স অ্যাক্সেস করা হয়, synchronized ব্লক ব্যবহার করতে হতে পারে।

  1. Semaphore একটি কার্যকরী টুল যা থ্রেড সিঙ্ক্রোনাইজেশন এবং রিসোর্স ব্যবস্থাপনা সহজ করে।
  2. এটি বিশেষভাবে উপযোগী যেখানে নির্দিষ্ট সংখ্যক থ্রেডকে রিসোর্স অ্যাক্সেস করার অনুমতি দিতে হয় (যেমন কানেকশন পুল বা বাউন্ডেড বাফার)।
  3. acquire() এবং release() মেথডগুলো সঠিকভাবে ব্যবহার করলে এটি ডেডলক এবং রিসোর্স ওভারলোড প্রতিরোধ করে।
Content added By
Promotion

Are you sure to start over?

Loading...