এই পাঠে, আমরা সাধারণত java.lang.ThreadLocal<> ক্লাসের সাথে কাজ করা এবং এটি একটি মাল্টিথ্রেড পরিবেশে কীভাবে ব্যবহার করা যায় সে সম্পর্কে আলোচনা করব।

ThreadLocal ক্লাসটি ভেরিয়েবল সংরক্ষণ করতে ব্যবহৃত হয় এই শ্রেণীর একটি স্বতন্ত্র বৈশিষ্ট্য হল এটি ব্যবহার করে প্রতিটি থ্রেডের জন্য একটি মানের একটি পৃথক স্বাধীন কপি রাখে।

ক্লাসের ক্রিয়াকলাপের আরও গভীরে গিয়ে, আমরা একটি মানচিত্র কল্পনা করতে পারি যা থ্রেডগুলিকে মানগুলির সাথে মানচিত্র করে, যেখান থেকে বর্তমান থ্রেডটি যখন এটি ব্যবহার করার প্রয়োজন হয় তখন উপযুক্ত মান নেয়৷

ThreadLocal ক্লাস কনস্ট্রাক্টর

কনস্ট্রাক্টর কর্ম
ThreadLocal() জাভাতে একটি খালি ভেরিয়েবল তৈরি করে

পদ্ধতি

পদ্ধতি কর্ম
পাওয়া() বর্তমান থ্রেডের স্থানীয় ভেরিয়েবলের মান প্রদান করে
সেট() বর্তমান থ্রেডের জন্য স্থানীয় ভেরিয়েবলের মান সেট করে
অপসারণ() বর্তমান থ্রেডের স্থানীয় ভেরিয়েবলের মান সরিয়ে দেয়
ThreadLocal.withInitial() অতিরিক্ত কারখানা পদ্ধতি যা প্রাথমিক মান সেট করে

পান() এবং সেট()

আসুন একটি উদাহরণ লিখি যেখানে আমরা দুটি কাউন্টার তৈরি করি। প্রথমটি, একটি সাধারণ পরিবর্তনশীল, থ্রেডের সংখ্যা গণনার জন্য হবে। দ্বিতীয়টি আমরা একটি ThreadLocal এ মোড়ানো হবে । এবং আমরা দেখব কিভাবে তারা একসাথে কাজ করে। প্রথমে, চলুন একটি থ্রেডডেমো ক্লাস লিখি যা রানেবলের উত্তরাধিকারী এবং এতে আমাদের ডেটা এবং সর্ব-গুরুত্বপূর্ণ রান() পদ্ধতি রয়েছে। আমরা স্ক্রিনে কাউন্টারগুলি প্রদর্শনের জন্য একটি পদ্ধতিও যোগ করব:


class ThreadDemo implements Runnable {

    int counter;
    ThreadLocal<Integer> threadLocalCounter = new ThreadLocal<>();

    public void run() {
        counter++;

        if(threadLocalCounter.get() != null) {
            threadLocalCounter.set(threadLocalCounter.get() + 1);
        } else {
            threadLocalCounter.set(0);
        }
        printCounters();
    }

    public void printCounters(){
        System.out.println("Counter: " + counter);
        System.out.println("threadLocalCounter: " + threadLocalCounter.get());
    }
}

আমাদের ক্লাসের প্রতিটি রানের সাথে, আমরা বৃদ্ধি করিপাল্টাভেরিয়েবল থ্রেডলোকাল ভেরিয়েবল থেকে ডেটা পেতে get() পদ্ধতিতে কল করে । যদি নতুন থ্রেডে কোনো ডেটা না থাকে, তাহলে আমরা এটিকে 0-এ সেট করব। যদি ডেটা থাকে, আমরা তা এক করে বাড়িয়ে দেব। এবং আমাদের প্রধান পদ্ধতি লিখুন :


public static void main(String[] args) {
    ThreadDemo threadDemo = new ThreadDemo();

    Thread t1 = new Thread(threadDemo);
    Thread t2 = new Thread(threadDemo);
    Thread t3 = new Thread(threadDemo);

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

}

আমাদের ক্লাস চালানোর সময়, আমরা দেখতে পাচ্ছি যে ThreadLocal ভেরিয়েবলটি যে থ্রেডটি অ্যাক্সেস করে তা নির্বিশেষে একই থাকে, কিন্তু থ্রেডের সংখ্যা বৃদ্ধি পায়।

কাউন্টার: 1
কাউন্টার: 2
কাউন্টার: 3
থ্রেডলোকাল কাউন্টার: 0
থ্রেডলোকাল কাউন্টার: 0
থ্রেডলোকাল কাউন্টার: 0

প্রস্থান কোড 0 দিয়ে প্রক্রিয়া শেষ হয়েছে

অপসারণ()

অপসারণ পদ্ধতি কীভাবে কাজ করে তা বোঝার জন্য , আমরা থ্রেডডেমো ক্লাসে কোডটি সামান্য পরিবর্তন করব :


if(threadLocalCounter.get() != null) {
      threadLocalCounter.set(threadLocalCounter.get() + 1);
  } else {
      if (counter % 2 == 0) {
          threadLocalCounter.remove();
      } else {
          threadLocalCounter.set(0);
      }
  }

এই কোডে, যদি থ্রেড কাউন্টার একটি জোড় সংখ্যা হয়, তাহলে আমরা আমাদের ThreadLocal ভেরিয়েবলে remove() মেথডকে কল করব। ফলাফল:

কাউন্টার: 3
থ্রেডলোকাল কাউন্টার: 0
কাউন্টার: 2
থ্রেডলোকাল কাউন্টার: নাল
কাউন্টার: 1
থ্রেডলোকাল কাউন্টার: 0

প্রস্থান কোড 0 দিয়ে প্রক্রিয়া শেষ হয়েছে

এবং এখানে আমরা সহজেই দেখতে পাচ্ছি যে দ্বিতীয় থ্রেডের ThreadLocal ভেরিয়েবলটি null

ThreadLocal.withInitial()

এই পদ্ধতিটি একটি থ্রেড-লোকাল ভেরিয়েবল তৈরি করে।

থ্রেডডেমো ক্লাসের বাস্তবায়ন :


class ThreadDemo implements Runnable {

    int counter;
    ThreadLocal<Integer> threadLocalCounter = ThreadLocal.withInitial(() -> 1);

    public void run() {
        counter++;
        printCounters();
    }

    public void printCounters(){
        System.out.println("Counter: " + counter);
        System.out.println("threadLocalCounter: " + threadLocalCounter.get());
    }
}

এবং আমরা আমাদের কোডের ফলাফল দেখতে পারি:

কাউন্টার: 1
কাউন্টার: 2
কাউন্টার: 3
থ্রেডলোকাল কাউন্টার: 1
থ্রেডলোকাল কাউন্টার: 1
থ্রেডলোকাল কাউন্টার: 1

প্রস্থান কোড 0 দিয়ে প্রক্রিয়া শেষ হয়েছে

কেন আমরা যেমন ভেরিয়েবল ব্যবহার করা উচিত?

থ্রেডলোকাল এক্সিকিউশন java.lang.Thread- এর থ্রেড সম্পর্কিত স্থানীয় ভেরিয়েবলের উপর একটি বিমূর্ততা প্রদান করে।

ThreadLocal ভেরিয়েবলগুলি সাধারণগুলির থেকে আলাদা যে প্রতিটি থ্রেডের নিজস্ব, পৃথকভাবে ভেরিয়েবলের প্রাথমিক ইনস্ট্যান্স রয়েছে, যা get() এবং set() পদ্ধতির মাধ্যমে অ্যাক্সেস করা হয়।

প্রতিটি থ্রেড, যেমন থ্রেড ক্লাসের উদাহরণ , এর সাথে যুক্ত ThreadLocal ভেরিয়েবলের একটি মানচিত্র রয়েছে। মানচিত্রের কীগুলি থ্রেডলোকাল অবজেক্টের রেফারেন্স এবং মানগুলি "অর্জিত" থ্রেডলোকাল ভেরিয়েবলের উল্লেখ ।

মাল্টিথ্রেডেড অ্যাপ্লিকেশনগুলিতে র্যান্ডম নম্বর তৈরি করার জন্য র্যান্ডম ক্লাস কেন উপযুক্ত নয়?

এলোমেলো সংখ্যা পেতে আমরা র্যান্ডম ক্লাস ব্যবহার করি। কিন্তু এটা কি মাল্টিথ্রেডেড পরিবেশে কাজ করে? আসলে না. র্যান্ডম মাল্টিথ্রেডেড পরিবেশের জন্য উপযুক্ত নয়, কারণ যখন একাধিক থ্রেড একই সময়ে একটি ক্লাস অ্যাক্সেস করে, কর্মক্ষমতা ক্ষতিগ্রস্ত হয়।

এই সমস্যা সমাধানের জন্য, JDK 7 একটি মাল্টিথ্রেড পরিবেশে র্যান্ডম সংখ্যা তৈরি করতে java.util.concurrent.ThreadLocalRandom ক্লাস চালু করেছে। এটি দুটি শ্রেণী নিয়ে গঠিত: ThreadLocal এবং Random

একটি থ্রেড দ্বারা প্রাপ্ত র্যান্ডম সংখ্যা অন্যান্য থ্রেড থেকে স্বাধীন, কিন্তু java.util.Random বিশ্বব্যাপী র্যান্ডম সংখ্যা প্রদান করে। এছাড়াও, Random এর বিপরীতে , ThreadLocalRandom সুস্পষ্ট বীজ বপন সমর্থন করে না। পরিবর্তে, এটি Random থেকে উত্তরাধিকারসূত্রে প্রাপ্ত setSeed() পদ্ধতিকে ওভাররাইড করে , যাতে কল করার সময় এটি সর্বদা একটি UnsupportedOperationException নিক্ষেপ করে।

আসুন ThreadLocalRandom ক্লাসের পদ্ধতিগুলি দেখি:

পদ্ধতি কর্ম
ThreadLocalRandom current() বর্তমান থ্রেডের ThreadLocalRandom প্রদান করে।
int পরবর্তী (int বিট) পরবর্তী ছদ্ম-এলোমেলো সংখ্যা তৈরি করে।
ডবল নেক্সট ডাবল (কমপক্ষে ডাবল, ডাবল বাউন্ড) ন্যূনতম (অন্তর্ভুক্ত) এবং আবদ্ধ (একচেটিয়া) মধ্যে একটি অভিন্ন বন্টন থেকে একটি ছদ্ম র্যান্ডম সংখ্যা প্রদান করে ।
int nextInt (অন্তত, int আবদ্ধ) ন্যূনতম (অন্তর্ভুক্ত) এবং আবদ্ধ (একচেটিয়া) মধ্যে একটি অভিন্ন বন্টন থেকে একটি ছদ্ম র্যান্ডম সংখ্যা প্রদান করে।
দীর্ঘ পরের দীর্ঘ (দীর্ঘ n) 0 (অন্তর্ভুক্ত) এবং নির্দিষ্ট মান (একচেটিয়া) এর মধ্যে একটি অভিন্ন বন্টন থেকে একটি ছদ্ম র্যান্ডম সংখ্যা প্রদান করে।
দীর্ঘ পরের দীর্ঘ (লম্বা নূন্যতম, দীর্ঘ আবদ্ধ) ন্যূনতম (অন্তর্ভুক্ত) এবং আবদ্ধ (একচেটিয়া) মধ্যে একটি অভিন্ন বন্টন থেকে একটি ছদ্ম র্যান্ডম সংখ্যা প্রদান করে।
অকার্যকর সেটবীজ (দীর্ঘ বীজ) অসমর্থিত অপারেশন এক্সেপশন নিক্ষেপ করে । এই জেনারেটর বীজ বপন সমর্থন করে না.

ThreadLocalRandom.current() ব্যবহার করে এলোমেলো নম্বর পাওয়া

ThreadLocalRandom হল ThreadLocal এবং Random ক্লাসের সমন্বয়। র‍্যান্ডম ক্লাসেরদৃষ্টান্তগুলিতে কোনো সমসাময়িক অ্যাক্সেস এড়িয়ে এটি একটি মাল্টিথ্রেডেড পরিবেশে আরও ভাল কর্মক্ষমতা অর্জন করে

আসুন একাধিক থ্রেড জড়িত একটি উদাহরণ প্রয়োগ করি এবং দেখুন আমাদের অ্যাপ্লিকেশনটি ThreadLocalRandom ক্লাসের সাথে করে:


import java.util.concurrent.ThreadLocalRandom;

class RandomNumbers extends Thread {

    public void run() {
        try {
            int bound = 100;
            int result = ThreadLocalRandom.current().nextInt(bound);
            System.out.println("Thread " + Thread.currentThread().getId() + " generated " + result);
        }
        catch (Exception e) {
            System.out.println("Exception");
        }
    }

    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();

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

        long endTime = System.currentTimeMillis();

        System.out.println("Time taken: " + (endTime - startTime));
    }
}

আমাদের প্রোগ্রামের ফলাফল:

সময় নেওয়া হয়েছে: 1
থ্রেড 17 জেনারেট করেছে 13
থ্রেড 18 জেনারেট করেছে 41
থ্রেড 16 জেনারেট করেছে 99
থ্রেড 19 জেনারেট করেছে 25
থ্রেড 23 জেনারেট করেছে 33
থ্রেড 24
জেনারেট করেছে 21 থ্রেড 15 জেনারেট করেছে 15 থ্রেড 21
থ্রেড 28
জেনার 27
থ্রেড 33

এবং এখন আসুন আমাদের RandomNumbers ক্লাস পরিবর্তন করি এবং এতে Random ব্যবহার করি:


int result = new Random().nextInt(bound);
সময় নেওয়া হয়েছে: 5
থ্রেড 20 জেনারেট করেছে 48
থ্রেড 19 জেনারেট করেছে 57
থ্রেড 18 জেনারেট
করেছে 90 থ্রেড 22 জেনারেট করেছে 43 থ্রেড
24 জেনারেট করেছে 7 থ্রেড 23 জেনারেট করেছে 63 থ্রেড 15 জেনারেট করেছে 2 থ্রেড 16 থ্রেড 19 থ্রেড 19 জেনারেট করেছে 2




নোট নাও! আমাদের পরীক্ষায়, কখনও কখনও ফলাফল একই ছিল এবং কখনও কখনও তারা ভিন্ন ছিল। কিন্তু যদি আমরা আরও থ্রেড ব্যবহার করি (বলুন, 100), ফলাফলটি এইরকম দেখাবে:

এলোমেলো — 19-25 ms
ThreadLocalRandom — 17-19 ms

তদনুসারে, আমাদের অ্যাপ্লিকেশনে যত বেশি থ্রেড থাকবে, মাল্টিথ্রেড পরিবেশে র্যান্ডম ক্লাস ব্যবহার করার সময় পারফরম্যান্স তত বেশি হবে ।

র‍্যান্ডম এবং থ্রেডলোকাল র‍্যান্ডম ক্লাসের মধ্যে পার্থক্যগুলিকে সংক্ষিপ্ত করতে এবং পুনরাবৃত্তি করতে :

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