Skill

জাভা কনকারেন্সি

জাভা প্রোগ্রামিং (Java Programming) - Computer Programming

314

Java তে কনকারেন্সি (Concurrency) হলো এমন একটি প্রক্রিয়া যা একাধিক কাজ বা থ্রেড একসাথে চালাতে সাহায্য করে। কনকারেন্সি মূলত বড় প্রোগ্রামের পারফরম্যান্স বাড়াতে সাহায্য করে, যেখানে একাধিক কাজ বা টাস্ক সমান্তরালে (parallel) অথবা স্বাধীনভাবে সম্পন্ন হতে পারে। Java তে কনকারেন্সির জন্য বিভিন্ন ক্লাস, ইন্টারফেস, এবং মেথড রয়েছে যা java.util.concurrent এবং java.lang প্যাকেজে পাওয়া যায়।


কনকারেন্সির মূল ধারণা

কনকারেন্সি ব্যবহারের মাধ্যমে একটি প্রোগ্রাম একাধিক কাজ একসাথে সম্পন্ন করতে পারে, ফলে পারফরম্যান্স বাড়ে এবং বড় প্রোগ্রাম দ্রুত সম্পন্ন হয়। Java তে কনকারেন্সির প্রধান বৈশিষ্ট্য হলো মাল্টি-থ্রেডিং। একাধিক থ্রেড ব্যবহার করে প্রোগ্রামের অংশগুলো একসাথে চালানো হয়।

থ্রেড এবং প্রসেসের পার্থক্য

  • প্রসেস: একটি স্বাধীন এক্সিকিউশন ইউনিট, যার নিজস্ব মেমোরি থাকে।
  • থ্রেড: একটি প্রসেসের অংশ, যা একই মেমোরি শেয়ার করে।

Java তে একটি প্রোগ্রামের একাধিক থ্রেড তৈরি করা সম্ভব, যা একসাথে কাজ করে প্রোগ্রামের কার্যক্ষমতা বাড়ায়।


থ্রেড তৈরি করার পদ্ধতি

Java তে থ্রেড তৈরি করার দুটি প্রধান পদ্ধতি রয়েছে:

  1. Thread ক্লাস প্রসারিত (Extend) করে
  2. Runnable ইন্টারফেস ইমপ্লিমেন্ট (Implement) করে

১. Thread ক্লাস প্রসারিত করে থ্রেড তৈরি করা

class MyThread extends Thread {
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + " - " + i);
        }
    }
}

public class ThreadExample {
    public static void main(String[] args) {
        MyThread thread1 = new MyThread();
        MyThread thread2 = new MyThread();

        thread1.start();
        thread2.start();
    }
}

আউটপুট:

Thread-0 - 0
Thread-1 - 0
Thread-0 - 1
Thread-1 - 1
...

২. Runnable ইন্টারফেস ইমপ্লিমেন্ট করে থ্রেড তৈরি করা

class MyRunnable implements Runnable {
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + " - " + i);
        }
    }
}

public class RunnableExample {
    public static void main(String[] args) {
        Thread thread1 = new Thread(new MyRunnable());
        Thread thread2 = new Thread(new MyRunnable());

        thread1.start();
        thread2.start();
    }
}

আউটপুট:

Thread-0 - 0
Thread-1 - 0
Thread-0 - 1
Thread-1 - 1
...

ব্যাখ্যা: এখানে MyRunnable ক্লাসটি Runnable ইন্টারফেস ইমপ্লিমেন্ট করে, এবং Thread অবজেক্ট তৈরি করে এটি চালানো হয়েছে।


কনকারেন্সি এবং থ্রেড সিঙ্ক্রোনাইজেশন

কনকারেন্সি ব্যবহারের সময় একাধিক থ্রেড একসাথে কাজ করার ফলে ডেটার সাথে সমস্যা তৈরি হতে পারে। একাধিক থ্রেড একসাথে একই ডেটা অ্যাক্সেস করতে পারে, যা Race Condition তৈরি করে। এই সমস্যা সমাধানের জন্য সিঙ্ক্রোনাইজেশন ব্যবহার করা হয়।

সিঙ্ক্রোনাইজড মেথড উদাহরণ

class Counter {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }
}

public class SynchronizationExample {
    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();

        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });

        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();

        System.out.println("Count: " + counter.getCount());
    }
}

আউটপুট:

Count: 2000

ব্যাখ্যা: এখানে increment() মেথডটি synchronized করে দেয়া হয়েছে, যার ফলে একবারে শুধুমাত্র একটি থ্রেড এই মেথড অ্যাক্সেস করতে পারে এবং রেস কন্ডিশন এড়ানো সম্ভব হয়।


java.util.concurrent প্যাকেজের গুরুত্বপূর্ণ ক্লাসসমূহ

Java কনকারেন্সি ব্যবস্থাপনা সহজ করতে java.util.concurrent প্যাকেজ প্রদান করে, যা নিম্নোক্ত গুরুত্বপূর্ণ ক্লাসগুলো অন্তর্ভুক্ত করে:

  1. ExecutorService: থ্রেড পুল ব্যবস্থাপনা করে এবং থ্রেড পুলের মাধ্যমে একাধিক কাজ পরিচালনা করে।
  2. Callable এবং Future: ফলাফল সহ কাজ সম্পন্ন করে, যেখানে Callable কাজ শেষ করার পর একটি ফলাফল প্রদান করে, এবং Future সেই ফলাফল ধারণ করে।
  3. CountDownLatch: নির্দিষ্ট সংখ্যক থ্রেড কাজ শেষ করার পর অবশিষ্ট কাজ চালায়।
  4. Semaphore: নির্দিষ্ট সংখ্যক থ্রেড একসাথে একটি রিসোর্স ব্যবহার করতে পারে।
  5. CyclicBarrier: নির্দিষ্ট সংখ্যক থ্রেড একত্রিত হলে কাজ চালায়।

ExecutorService এবং থ্রেড পুল ব্যবহার উদাহরণ

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExecutorExample {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(3);

        for (int i = 0; i < 5; i++) {
            executor.submit(() -> {
                System.out.println(Thread.currentThread().getName() + " is running a task.");
            });
        }

        executor.shutdown();
    }
}

আউটপুট:

pool-1-thread-1 is running a task.
pool-1-thread-2 is running a task.
pool-1-thread-3 is running a task.
...

ব্যাখ্যা: এখানে ExecutorService ব্যবহার করে একটি থ্রেড পুল তৈরি করা হয়েছে, যা একই সাথে একাধিক থ্রেডের মাধ্যমে কাজ সম্পন্ন করে।


কনকারেন্সি ব্যবহারের সুবিধা এবং চ্যালেঞ্জ

সুবিধা:

  1. বর্ধিত কার্যক্ষমতা: একাধিক থ্রেড ব্যবহার করে কাজ দ্রুত সম্পন্ন করা যায়।
  2. রিসোর্সের কার্যকরী ব্যবহার: একই রিসোর্সে একাধিক থ্রেড কাজ করতে পারে।
  3. সহজ কনকারেন্ট প্রোগ্রামিং: java.util.concurrent প্যাকেজ ব্যবহার করে কনকারেন্ট প্রোগ্রামিং সহজে করা যায়।

চ্যালেঞ্জ:

  1. ডেটা ইনকনসিস্টেন্সি: একাধিক থ্রেড একই ডেটা অ্যাক্সেস করলে সমস্যা তৈরি হতে পারে।
  2. Deadlock এবং Race Condition: থ্রেড ব্যবস্থাপনায় ডেডলক এবং রেস কন্ডিশনের সমস্যা তৈরি হতে পারে।
  3. কমপ্লেক্সিটি: কনকারেন্ট প্রোগ্রামিং পরিচালনা করা কঠিন এবং জটিল হতে পারে।

সারসংক্ষেপ

Java তে কনকারেন্সি বড় প্রোগ্রামের কার্যক্ষমতা বাড়াতে সহায়ক। একাধিক থ্রেড ব্যবহার করে একসাথে কাজ করার মাধ্যমে প্রোগ্রাম আরও দ্রুত এবং কার্যকরী হয়। java.util.concurrent প্যাকেজের বিভিন্ন ক্লাস কনকারেন্ট প্রোগ্রামিং সহজে করতে সাহায্য করে। সিঙ্ক্রোনাইজেশন এবং থ্রেড পুল ব্যবস্থাপনা করে কনকারেন্ট প্রোগ্রামিং আরও কার্যকরী করা যায়, তবে ডেডলক এবং রেস কন্ডিশনের মতো সমস্যাগুলি সঠিকভাবে পরিচালনা করতে সতর্ক থাকতে হয়।

Content added By

থ্রেড (Thread) হলো Java প্রোগ্রামিং ভাষার একটি কার্যপ্রণালী, যা প্রোগ্রামের ভিতরে ছোট ছোট সাব-প্রসেস তৈরি করে কাজ সম্পন্ন করতে সক্ষম। Java তে থ্রেড হলো একটি স্বতন্ত্র এক্সিকিউশন ইউনিট, অর্থাৎ এটি নিজস্ব একটি নির্দিষ্ট কাজ করতে পারে এবং প্রোগ্রামের মূল কাজের সাথে একযোগে (concurrently) চালিত হতে পারে।

Java তে Thread ক্লাস এবং Runnable ইন্টারফেস ব্যবহার করে থ্রেড তৈরি এবং পরিচালনা করা যায়।


কেন থ্রেড প্রয়োজন?

  1. প্যারালালিজম (Parallelism): থ্রেড ব্যবহার করে প্রোগ্রামের একাধিক কাজ একসাথে চালানো সম্ভব। উদাহরণস্বরূপ, একটি প্রোগ্রাম ফাইল ডাউনলোড এবং ইউজার ইন্টারফেস আপডেট একই সাথে করতে পারে।
  2. CPU এর সর্বোচ্চ ব্যবহার: থ্রেড ব্যবহারের মাধ্যমে কম্পিউটার প্রসেসরের বহুমুখী অংশগুলোর (cores) সর্বোচ্চ ব্যবহার করা যায়, যা প্রোগ্রামের গতি বাড়ায়।
  3. উচ্চ কার্যক্ষমতা (Performance): থ্রেডিং ব্যবহারের ফলে প্রোগ্রাম অপেক্ষাকৃত দ্রুততর কাজ করতে পারে, কারণ বিভিন্ন কাজ আলাদাভাবে পরিচালিত হয়।
  4. ব্যবহারকারী অভিজ্ঞতা উন্নত করা: থ্রেড ব্যবহার করে বড় এবং জটিল প্রোগ্রামগুলোর কাজ একযোগে সম্পন্ন করে, যাতে ব্যবহারকারীর জন্য প্রোগ্রাম আরও দ্রুত এবং ব্যবহার উপযোগী হয়।

থ্রেড কিভাবে কাজ করে?

Java তে Thread ক্লাস অথবা Runnable ইন্টারফেস ব্যবহার করে থ্রেড তৈরি করা যায়। start() মেথড ব্যবহার করে থ্রেড চালু করা হয়, যা থ্রেডের run() মেথডকে কল করে।

থ্রেডের প্রধান স্টেট বা অবস্থা

  1. New: থ্রেড তৈরি হয়েছে, কিন্তু শুরু হয়নি।
  2. Runnable: থ্রেড চালু হওয়ার জন্য প্রস্তুত আছে বা CPU সময়ের জন্য অপেক্ষা করছে।
  3. Blocked: থ্রেড চলমান নয় এবং এটি কোনো রিসোর্সের জন্য অপেক্ষা করছে।
  4. Waiting: থ্রেড চালু হয়নি এবং অন্য থ্রেডের সিগন্যালের জন্য অপেক্ষা করছে।
  5. Terminated: থ্রেডের কাজ শেষ হয়েছে।

থ্রেড তৈরি করার উপায়

১. Thread ক্লাস এক্সটেন্ড করে থ্রেড তৈরি

class MyThread extends Thread {
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println(Thread.currentThread().getName() + " - Count: " + i);
            try {
                Thread.sleep(500); // ৫০০ মিলিসেকেন্ডের জন্য থ্রেড থামানো
            } catch (InterruptedException e) {
                System.out.println("Thread interrupted: " + e.getMessage());
            }
        }
    }
}

public class ThreadExample {
    public static void main(String[] args) {
        MyThread thread1 = new MyThread();
        MyThread thread2 = new MyThread();

        thread1.start(); // থ্রেড ১ শুরু
        thread2.start(); // থ্রেড ২ শুরু
    }
}

আউটপুট:

Thread-0 - Count: 1
Thread-1 - Count: 1
Thread-0 - Count: 2
Thread-1 - Count: 2
...

ব্যাখ্যা:

  • MyThread ক্লাসটি Thread ক্লাস এক্সটেন্ড করে তৈরি করা হয়েছে।
  • start() মেথড ব্যবহার করে thread1 এবং thread2 চালু করা হয়েছে, যা run() মেথডে সংজ্ঞায়িত কাজ সম্পন্ন করে।

২. Runnable ইন্টারফেস ইমপ্লিমেন্ট করে থ্রেড তৈরি

class MyRunnable implements Runnable {
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println(Thread.currentThread().getName() + " - Count: " + i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                System.out.println("Thread interrupted: " + e.getMessage());
            }
        }
    }
}

public class RunnableExample {
    public static void main(String[] args) {
        Thread thread1 = new Thread(new MyRunnable());
        Thread thread2 = new Thread(new MyRunnable());

        thread1.start(); // থ্রেড ১ শুরু
        thread2.start(); // থ্রেড ২ শুরু
    }
}

ব্যাখ্যা:

  • MyRunnable ক্লাস Runnable ইন্টারফেস ইমপ্লিমেন্ট করে।
  • Thread ক্লাসের কন্সট্রাক্টরে MyRunnable পাস করা হয়েছে এবং start() মেথডের মাধ্যমে থ্রেডগুলো চালু করা হয়েছে।

থ্রেড সংক্রান্ত গুরুত্বপূর্ণ মেথড

  1. start(): থ্রেড শুরু করার জন্য ব্যবহৃত হয়, যা run() মেথড কল করে।
  2. run(): থ্রেড চালু হলে এর কার্যপ্রণালী সম্পাদন করে।
  3. sleep(long milliseconds): থ্রেডকে নির্দিষ্ট সময়ের জন্য থামানো হয়, যা InterruptedException থ্রো করতে পারে।
  4. join(): থ্রেড শেষ না হওয়া পর্যন্ত অন্য থ্রেডকে অপেক্ষা করানো হয়।
  5. yield(): বর্তমান থ্রেড সাময়িকভাবে CPU ছেড়ে দিয়ে অন্য থ্রেডকে সুযোগ দেয়।
  6. interrupt(): থ্রেডকে থামানোর জন্য সিগন্যাল প্রদান করা হয়।

উদাহরণ: join() মেথড ব্যবহার

class MyThread extends Thread {
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println(Thread.currentThread().getName() + " - Count: " + i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                System.out.println("Thread interrupted: " + e.getMessage());
            }
        }
    }
}

public class JoinExample {
    public static void main(String[] args) {
        MyThread thread1 = new MyThread();
        MyThread thread2 = new MyThread();

        thread1.start();
        try {
            thread1.join(); // থ্রেড ১ শেষ না হওয়া পর্যন্ত থ্রেড ২ অপেক্ষা করবে
        } catch (InterruptedException e) {
            System.out.println("Main thread interrupted: " + e.getMessage());
        }
        thread2.start();
    }
}

ব্যাখ্যা:

  • thread1.join() কল করার ফলে thread1 শেষ না হওয়া পর্যন্ত thread2 অপেক্ষা করবে।

থ্রেডের সুবিধা এবং সীমাবদ্ধতা

সুবিধা

  1. প্যারালাল প্রসেসিং: একাধিক কাজ একসাথে চালানো যায়।
  2. CPU ব্যবহার বৃদ্ধি: CPU-এর বিভিন্ন কোরে আলাদা থ্রেড পরিচালনা করা যায়।
  3. উচ্চ কার্যক্ষমতা: থ্রেডের মাধ্যমে বড় কাজ ছোট ছোট অংশে ভাগ করে দ্রুত কাজ সম্পন্ন করা যায়।
  4. ব্যবহারকারীর অভিজ্ঞতা উন্নত: থ্রেড ব্যবহার করে দীর্ঘমেয়াদী কাজের জন্য প্রোগ্রাম বন্ধ না করে ব্যাকগ্রাউন্ডে চালানো যায়।

সীমাবদ্ধতা

  1. কমপ্লেক্সিটি বৃদ্ধি: থ্রেড পরিচালনা জটিল এবং এটি সঠিকভাবে ব্যবহার না করলে ডেটা অসঙ্গতি ঘটতে পারে।
  2. ডেডলক (Deadlock): একাধিক থ্রেড একে অপরের জন্য অপেক্ষা করতে পারে, ফলে প্রোগ্রাম আটকে যেতে পারে।
  3. রিসোর্স ব্যবহারের সীমাবদ্ধতা: অতিরিক্ত থ্রেড ব্যবহারে মেমোরি ও প্রসেসিং পাওয়ার দ্রুত শেষ হয়ে যেতে পারে।

সারসংক্ষেপ

  • থ্রেড হলো প্রোগ্রামের একটি আলাদা এক্সিকিউশন ইউনিট, যা প্যারালাল কাজ সম্পাদন করতে ব্যবহৃত হয়।
  • Java তে থ্রেড তৈরি করতে Thread ক্লাস বা Runnable ইন্টারফেস ব্যবহার করা হয়।
  • থ্রেড ব্যবহারে প্যারালাল প্রসেসিং, উচ্চ কার্যক্ষমতা, এবং উন্নত ব্যবহারকারীর অভিজ্ঞতা নিশ্চিত হয়।
  • থ্রেড ব্যবহারে সতর্ক থাকতে হয়, কারণ ডেডলক এবং ডেটা অসঙ্গতি ঘটার ঝুঁকি থাকে।

থ্রেড ব্যবহার করে বড় ও জটিল প্রোগ্রামগুলোর কাজ দ্রুত এবং কার্যকরভাবে সম্পাদন করা সম্ভব, যা আধুনিক সফটওয়্যার ডেভেলপমেন্টে অত্যন্ত গুরুত্বপূর্ণ।

Content added By

Java তে থ্রেড তৈরি এবং ম্যানেজ করা একটি গুরুত্বপূর্ণ কাজ, যা প্রোগ্রামের পারফরম্যান্স এবং কার্যক্ষমতা বাড়াতে সাহায্য করে। মাল্টি-থ্রেডিংয়ের মাধ্যমে একটি প্রোগ্রামের একাধিক কাজ একসাথে সম্পন্ন করা সম্ভব হয়। Java তে থ্রেড তৈরি করার জন্য মূলত Thread ক্লাস এবং Runnable ইন্টারফেস ব্যবহৃত হয়।


থ্রেড কী?

থ্রেড হলো একটি ছোট এক্সিকিউশন ইউনিট যা প্রোগ্রামের ভেতরে একাধিক কাজ একসাথে সম্পন্ন করতে সহায়ক। Java তে Thread ক্লাস এবং Runnable ইন্টারফেস ব্যবহার করে থ্রেড তৈরি করা যায়।


থ্রেড তৈরি করার পদ্ধতি

Java তে থ্রেড তৈরি করার প্রধান দুটি পদ্ধতি রয়েছে:

  1. Thread ক্লাস প্রসারিত (Extend) করে
  2. Runnable ইন্টারফেস ইমপ্লিমেন্ট (Implement) করে

১. Thread ক্লাস প্রসারিত করে থ্রেড তৈরি করা

Thread ক্লাস প্রসারিত করে নতুন থ্রেড তৈরি করা যায়, যেখানে run() মেথড ওভাররাইড করে নির্দিষ্ট কাজ সম্পন্ন করা হয়।

class MyThread extends Thread {
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + " - " + i);
        }
    }
}

public class ThreadExample {
    public static void main(String[] args) {
        MyThread thread1 = new MyThread();
        MyThread thread2 = new MyThread();

        thread1.start(); // থ্রেড শুরু করা
        thread2.start();
    }
}

ব্যাখ্যা:

  • MyThread ক্লাসটি Thread ক্লাস প্রসারিত করে এবং run() মেথড ওভাররাইড করে কাজ সম্পন্ন করেছে।
  • start() মেথড থ্রেড চালায়, যার ফলে run() মেথড কার্যকর হয়।

২. Runnable ইন্টারফেস ইমপ্লিমেন্ট করে থ্রেড তৈরি করা

Runnable ইন্টারফেস ইমপ্লিমেন্ট করে থ্রেড তৈরি করা যায়, যা Java-র অন্যতম জনপ্রিয় পদ্ধতি। এই পদ্ধতিতে run() মেথড ওভাররাইড করতে হয় এবং Thread অবজেক্টের মাধ্যমে থ্রেড চালানো হয়।

class MyRunnable implements Runnable {
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + " - " + i);
        }
    }
}

public class RunnableExample {
    public static void main(String[] args) {
        Thread thread1 = new Thread(new MyRunnable());
        Thread thread2 = new Thread(new MyRunnable());

        thread1.start(); // থ্রেড শুরু করা
        thread2.start();
    }
}

ব্যাখ্যা:

  • MyRunnable ক্লাস Runnable ইন্টারফেস ইমপ্লিমেন্ট করেছে এবং run() মেথড ওভাররাইড করেছে।
  • নতুন Thread অবজেক্ট তৈরি করে start() মেথড ব্যবহার করে থ্রেড চালানো হয়েছে।

থ্রেড ম্যানেজমেন্টের কিছু গুরুত্বপূর্ণ মেথড

Java তে থ্রেড ম্যানেজমেন্টের জন্য বিভিন্ন মেথড রয়েছে, যা থ্রেডের স্টেটস নিয়ন্ত্রণ করতে এবং কার্যক্ষমতা বাড়াতে সহায়ক:

  1. start(): থ্রেড চালু করার জন্য ব্যবহৃত হয় এবং run() মেথড কার্যকর হয়।
  2. run(): থ্রেডে সম্পন্ন করতে ইচ্ছুক কাজ নির্ধারণ করে।
  3. sleep(long milliseconds): নির্দিষ্ট সময়ের জন্য থ্রেডকে থামিয়ে দেয়।
  4. join(): অন্য থ্রেড শেষ না হওয়া পর্যন্ত বর্তমান থ্রেডকে অপেক্ষা করতে বাধ্য করে।
  5. interrupt(): থ্রেডকে থামানোর সংকেত দেয়।
  6. isAlive(): থ্রেড সক্রিয় কিনা তা পরীক্ষা করে।

উদাহরণ: sleep() এবং join() মেথড ব্যবহার

class MyThread extends Thread {
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println(Thread.currentThread().getName() + " - " + i);
            try {
                Thread.sleep(1000); // ১ সেকেন্ড থামিয়ে দেয়
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class ThreadSleepJoinExample {
    public static void main(String[] args) throws InterruptedException {
        MyThread thread1 = new MyThread();
        MyThread thread2 = new MyThread();

        thread1.start();
        thread1.join(); // thread1 শেষ না হওয়া পর্যন্ত অপেক্ষা করে

        thread2.start();
    }
}

ব্যাখ্যা:

  • sleep(1000) মেথড ব্যবহার করে প্রতিবার ১ সেকেন্ড থ্রেডকে থামিয়ে দেয়।
  • join() মেথড thread1 সম্পন্ন না হওয়া পর্যন্ত thread2 কে অপেক্ষা করায়।

থ্রেড পুল ব্যবহারের মাধ্যমে থ্রেড ম্যানেজমেন্ট

থ্রেড পুল হলো থ্রেড ম্যানেজমেন্টের একটি পদ্ধতি, যা থ্রেড পুনর্ব্যবহারযোগ্য করে। থ্রেড পুল ব্যবহার করলে প্রতিবার নতুন থ্রেড তৈরি না করে বিদ্যমান থ্রেড পুনরায় ব্যবহার করা যায়, যা সিস্টেমের কার্যক্ষমতা বাড়ায়।

ExecutorService এবং থ্রেড পুল উদাহরণ

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

class Task implements Runnable {
    public void run() {
        System.out.println(Thread.currentThread().getName() + " is executing a task.");
    }
}

public class ThreadPoolExample {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(3); // ৩টি থ্রেডের পুল তৈরি

        for (int i = 0; i < 5; i++) {
            executor.submit(new Task()); // প্রতিটি টাস্ক সাবমিট করা
        }

        executor.shutdown(); // থ্রেড পুল বন্ধ করা
    }
}

আউটপুট:

pool-1-thread-1 is executing a task.
pool-1-thread-2 is executing a task.
pool-1-thread-3 is executing a task.
...

ব্যাখ্যা:

  • এখানে Executors.newFixedThreadPool(3) ব্যবহার করে ৩টি থ্রেডের একটি পুল তৈরি করা হয়েছে।
  • submit() মেথড ব্যবহার করে থ্রেড পুলে টাস্ক যুক্ত করা হয়েছে।

থ্রেড ম্যানেজমেন্টের চ্যালেঞ্জসমূহ

  1. Deadlock: একাধিক থ্রেড একে অপরের উপর নির্ভরশীল হলে deadlock তৈরি হয়, যা থ্রেড স্থবির করে দেয়।
  2. Race Condition: একাধিক থ্রেড একই ডেটা একসাথে পরিবর্তন করতে চাইলে ডেটা অসঙ্গতি ঘটে।
  3. সিঙ্ক্রোনাইজেশন প্রয়োজন: একাধিক থ্রেড ডেটা শেয়ার করলে সঠিকভাবে সিঙ্ক্রোনাইজ করা প্রয়োজন, যাতে ডেটা ম্যানিপুলেশন নির্ভুল হয়।

থ্রেড নিরাপত্তা নিশ্চিত করতে সিঙ্ক্রোনাইজেশন

একাধিক থ্রেড একই ডেটা অ্যাক্সেস করলে ডেটা ইনকনসিস্টেন্সি সমস্যা তৈরি হতে পারে। এই সমস্যা সমাধানের জন্য সিঙ্ক্রোনাইজেশন ব্যবহার করা হয়।

class Counter {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }
}

public class SynchronizedExample {
    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();

        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });

        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();

        System.out.println("Final count: " + counter.getCount());
    }
}

আউটপুট:

Final count: 2000

ব্যাখ্যা: increment() মেথডটি synchronized করে দেয়া হয়েছে, যার ফলে একবারে শুধুমাত্র একটি থ্রেড এই মেথড অ্যাক্সেস করতে পারে, এবং ডেটা ইনকনসিস্টেন্সি এড়ানো সম্ভব হয়েছে।


সারসংক্ষেপ

Java তে থ্রেড তৈরি এবং ম্যানেজ করা বড় প্রোগ্রাম বা অ্যাপ্লিকেশনের কার্যক্ষমতা বাড়ায়। Thread ক্লাস বা Runnable ইন্টারফেস ব্যবহার করে থ্রেড তৈরি করা যায়। ExecutorService এবং থ্রেড পুল ব্যবহার করে কার্যক্ষম থ্রেড ম্যানেজমেন্ট নিশ্চিত করা যায়। থ্রেড নিরাপত্তার জন্য সিঙ্ক্রোনাইজেশন এবং deadlock বা race condition এর মতো সমস্যা এড়াতে সতর্ক থাকা প্রয়োজন। Java কনকারেন্সির সঠিক ব্যবহার বড় অ্যাপ্লিকেশনে পারফরম্যান্স বাড়াতে সহায়ক।

Content added By

সিঙ্ক্রোনাইজেশন (Synchronization) হলো Java তে একাধিক থ্রেডের মধ্যে ডেটা অ্যাক্সেস ও পরিবর্তনের নিয়ন্ত্রণ করার একটি প্রক্রিয়া, যাতে থ্রেডগুলো সমন্বিতভাবে (সিঙ্ক্রোনাইজড) কাজ করতে পারে। সিঙ্ক্রোনাইজেশন বিশেষত তখন ব্যবহৃত হয় যখন একাধিক থ্রেড একই রিসোর্সে (যেমন একটি ভ্যারিয়েবল বা অবজেক্ট) একযোগে অ্যাক্সেস বা পরিবর্তন করার চেষ্টা করে। এটি ডেটা ইনকনসিস্টেন্সি বা অনির্দিষ্ট ফলাফল থেকে প্রোগ্রামকে রক্ষা করতে সহায়ক।


সিঙ্ক্রোনাইজেশন কেন প্রয়োজন?

  1. ডেটা ইনকনসিস্টেন্সি এড়াতে: যখন একাধিক থ্রেড একসাথে একই ডেটায় কাজ করে, তখন ডেটা ইনকনসিস্টেন্ট হতে পারে। সিঙ্ক্রোনাইজেশন নিশ্চিত করে যে একটি থ্রেড ডেটা পরিবর্তন সম্পূর্ণ না করা পর্যন্ত অন্য কোনো থ্রেড সেই ডেটা অ্যাক্সেস করতে পারবে না।
  2. ডেডলক এবং রেস কন্ডিশন এড়াতে: একাধিক থ্রেডের মধ্যে রিসোর্স ব্যবহারের সংঘর্ষ এড়ানো যায়, যা ডেডলক এবং রেস কন্ডিশনের সমস্যা এড়াতে সহায়ক।
  3. ডেটা সুরক্ষা: সিঙ্ক্রোনাইজেশন ব্যবহারে ডেটা সুরক্ষিত থাকে, বিশেষত সংবেদনশীল তথ্য ব্যবহারের সময়।

সিঙ্ক্রোনাইজেশন কিভাবে কাজ করে?

Java তে synchronized কীওয়ার্ড ব্যবহার করে সিঙ্ক্রোনাইজেশন করা হয়। এটি মূলত মেথড এবং ব্লক এই দুইভাবে ব্যবহার করা যায়।

  1. সিঙ্ক্রোনাইজড মেথড (Synchronized Method): সম্পূর্ণ মেথডকে সিঙ্ক্রোনাইজড করা হয়।
  2. সিঙ্ক্রোনাইজড ব্লক (Synchronized Block): মেথডের একটি নির্দিষ্ট অংশ সিঙ্ক্রোনাইজড করা হয়।

সিঙ্ক্রোনাইজড মেথড উদাহরণ

class Counter {
    private int count = 0;

    // synchronized মেথড
    public synchronized void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }
}

public class SynchronizedMethodExample {
    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();

        // দুইটি থ্রেড তৈরি এবং চালু করা
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });

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

        t1.join();
        t2.join();

        System.out.println("Final Count: " + counter.getCount());
    }
}

আউটপুট:

Final Count: 2000

ব্যাখ্যা:

  • increment() মেথডে synchronized কীওয়ার্ড ব্যবহার করা হয়েছে, ফলে একাধিক থ্রেড এই মেথডে একযোগে প্রবেশ করতে পারবে না।
  • t1 এবং t2 থ্রেড একই অবজেক্টের increment() মেথডে কাজ করার কারণে, এটি সঠিক ফলাফল প্রদান করে।

সিঙ্ক্রোনাইজড ব্লক উদাহরণ

কখনও কখনও সম্পূর্ণ মেথড সিঙ্ক্রোনাইজ করা অপ্রয়োজনীয় হতে পারে, তখন নির্দিষ্ট ব্লক সিঙ্ক্রোনাইজ করা হয়।

class Counter {
    private int count = 0;

    public void increment() {
        synchronized (this) {
            count++;
        }
    }

    public int getCount() {
        return count;
    }
}

public class SynchronizedBlockExample {
    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();

        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });

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

        t1.join();
        t2.join();

        System.out.println("Final Count: " + counter.getCount());
    }
}

ব্যাখ্যা:

  • synchronized (this) ব্লকের মাধ্যমে কেবল count++ অপারেশনটি সিঙ্ক্রোনাইজ করা হয়েছে।
  • এটি নিশ্চিত করে যে শুধুমাত্র count পরিবর্তনের সময় থ্রেডগুলো সমন্বিতভাবে কাজ করবে।

স্ট্যাটিক মেথডে সিঙ্ক্রোনাইজেশন

স্ট্যাটিক মেথডে সিঙ্ক্রোনাইজেশন করতে synchronized কীওয়ার্ড ব্যবহার করা হয়, তবে এটি this অবজেক্টের পরিবর্তে ক্লাস অবজেক্টে কাজ করে।

class StaticCounter {
    private static int count = 0;

    public static synchronized void increment() {
        count++;
    }

    public static int getCount() {
        return count;
    }
}

ব্যাখ্যা:

  • স্ট্যাটিক মেথডে synchronized ব্যবহার করলে এটি ক্লাস অবজেক্টে সিঙ্ক্রোনাইজড হয়, ফলে স্ট্যাটিক ভেরিয়েবল সঠিকভাবে আপডেট হয়।

সিঙ্ক্রোনাইজেশন সম্পর্কিত গুরুত্বপূর্ণ বিষয়

  1. মনিটর লক (Monitor Lock): যখন কোনো থ্রেড সিঙ্ক্রোনাইজড মেথড বা ব্লকে প্রবেশ করে, তখন সে মনিটর লক (Monitor Lock) অর্জন করে। লক না পেলে থ্রেড অপেক্ষা করে।
  2. ডেডলক (Deadlock): যদি দুটি থ্রেড একে অপরের জন্য অপেক্ষা করে, তাহলে এটি ডেডলক সৃষ্টি করে। ডেডলক এড়ানোর জন্য সঠিকভাবে সিঙ্ক্রোনাইজেশন করা গুরুত্বপূর্ণ।
  3. পারফরম্যান্সে প্রভাব: সিঙ্ক্রোনাইজড ব্লক বা মেথড থ্রেডের একসঙ্গে প্রবেশের সুযোগ কমায়, ফলে পারফরম্যান্সে কিছুটা প্রভাব ফেলে। তাই প্রয়োজন ছাড়া সিঙ্ক্রোনাইজেশন ব্যবহার এড়ানো উচিত।

সিঙ্ক্রোনাইজেশন সংক্রান্ত সুবিধা এবং সীমাবদ্ধতা

সুবিধা

  1. ডেটা সুরক্ষা নিশ্চিত: একাধিক থ্রেডের মধ্যে সমন্বিত ডেটা অ্যাক্সেস নিশ্চিত করে।
  2. রেস কন্ডিশন প্রতিরোধ: রেস কন্ডিশনের কারণে ডেটা ইনকনসিস্টেন্সি প্রতিরোধ করে।
  3. একযোগে অ্যাক্সেস নিয়ন্ত্রণ: একাধিক থ্রেডের সমন্বিত কাজ নিশ্চিত করে।

সীমাবদ্ধতা

  1. পারফরম্যান্সে প্রভাব: অতিরিক্ত সিঙ্ক্রোনাইজেশন ব্যবহারে প্রোগ্রামের গতি কমে যেতে পারে।
  2. ডেডলক ঝুঁকি: সঠিকভাবে সিঙ্ক্রোনাইজ করা না হলে ডেডলক হতে পারে, যা থ্রেডগুলিকে অনির্দিষ্টকালের জন্য আটকে রাখতে পারে।
  3. কোড জটিলতা: সিঙ্ক্রোনাইজেশন ব্যবহারে কোড জটিল হতে পারে এবং ডিবাগিং কঠিন হতে পারে।

সারসংক্ষেপ

  • সিঙ্ক্রোনাইজেশন হলো একাধিক থ্রেডের মধ্যে ডেটা অ্যাক্সেস ও পরিবর্তনের নিয়ন্ত্রণ, যা ডেটা ইনকনসিস্টেন্সি প্রতিরোধে সহায়ক।
  • Java তে synchronized কীওয়ার্ড ব্যবহার করে মেথড বা ব্লককে সিঙ্ক্রোনাইজ করা যায়।
  • সঠিকভাবে সিঙ্ক্রোনাইজেশন ব্যবহারে ডেটা সুরক্ষা ও সঠিকতা নিশ্চিত হয়, তবে এটি ব্যবহারে পারফরম্যান্স এবং ডেডলকের মতো সমস্যা হতে পারে।

Java প্রোগ্রামিংয়ে সিঙ্ক্রোনাইজেশন ব্যবহার করে থ্রেড সেফটি নিশ্চিত করা যায়, যা বহুমুখী এবং জটিল প্রোগ্রামগুলোর জন্য অত্যন্ত গুরুত্বপূর্ণ।

Content added By
Promotion

Are you sure to start over?

Loading...