এক্সিকিউটর ক্লাসের নতুন ফিক্সড থ্রেডপুল পদ্ধতি একটি নির্দিষ্ট সংখ্যক থ্রেড সহ একটি এক্সিকিউটরসার্ভিস তৈরি করে NewSingleThreadExecutor পদ্ধতির বিপরীতে , আমরা পুলে কতগুলি থ্রেড চাই তা নির্দিষ্ট করি। হুড অধীনে, নিম্নলিখিত কোড বলা হয়:

new ThreadPoolExecutor(nThreads, nThreads,
                                      	0L, TimeUnit.MILLISECONDS,
                                      	new LinkedBlockingQueue());

corePoolSize ( এক্সিকিউটর পরিষেবা শুরু হলে যে থ্রেডগুলি প্রস্তুত হবে (শুরু হবে ) ) _ এবং আমরা ঠিক একই ভাবে ThreadFactory এর নিজস্ব বাস্তবায়ন পাস করতে পারি ।

আচ্ছা, আসুন দেখি কেন আমাদের এমন একটি ExecutorService দরকার ।

এখানে থ্রেডের একটি নির্দিষ্ট সংখ্যা (n) সহ একটি ExecutorService এর যুক্তি রয়েছে :

  • প্রক্রিয়াকরণের কাজগুলির জন্য সর্বাধিক n থ্রেড সক্রিয় থাকবে।
  • যদি n-এর বেশি কাজ জমা দেওয়া হয়, থ্রেড মুক্ত না হওয়া পর্যন্ত সেগুলিকে সারিতে রাখা হবে।
  • যদি থ্রেডগুলির মধ্যে একটি ব্যর্থ হয় এবং বন্ধ হয়ে যায়, তবে তার জায়গা নেওয়ার জন্য একটি নতুন থ্রেড তৈরি করা হবে।
  • পুল বন্ধ না হওয়া পর্যন্ত পুলের যেকোনো থ্রেড সক্রিয় থাকে।

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

আমরা অবিলম্বে নোট করব যে শর্তগুলি আদর্শ হলেও — প্রতিশ্রুত n থ্রেডগুলি স্থিরভাবে কাজ করে এবং একটি ত্রুটির সাথে শেষ হওয়া থ্রেডগুলি সর্বদা প্রতিস্থাপিত হয় (এমন কিছু যা সীমিত সংস্থানগুলি একটি বাস্তব বিমানবন্দরে অর্জন করা অসম্ভব করে তোলে) — সিস্টেমে এখনও বেশ কয়েকটি রয়েছে অপ্রীতিকর বৈশিষ্ট্য, কারণ কোনও পরিস্থিতিতেই বেশি থ্রেড থাকবে না, এমনকি যদি সারিটি থ্রেডগুলি কাজগুলি প্রক্রিয়া করতে পারে তার চেয়ে দ্রুত বৃদ্ধি পায়।

একটি নির্দিষ্ট সংখ্যক থ্রেডের সাথে ExecutorService কিভাবে কাজ করে তার একটি ব্যবহারিক বোঝার জন্য আমি পরামর্শ দিচ্ছি । চলুন একটি ক্লাস তৈরি করি যা Runnable প্রয়োগ করে । এই শ্রেণীর অবজেক্টগুলি ExecutorService-এর জন্য আমাদের কাজগুলিকে উপস্থাপন করে

public class Task implements Runnable {
    int taskNumber;

    public Task(int taskNumber) {
        this.taskNumber = taskNumber;
    }

    @Override
    public void run() {
try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Processed user request #" + taskNumber + " on thread " + Thread.currentThread().getName());
    }
}

রান () পদ্ধতিতে, আমরা থ্রেডটিকে 2 সেকেন্ডের জন্য ব্লক করি, কিছু কাজের চাপ অনুকরণ করে, এবং তারপরে বর্তমান টাস্কের নম্বর এবং টাস্কটি সম্পাদনকারী থ্রেডের নাম প্রদর্শন করি।

ExecutorService executorService = Executors.newFixedThreadPool(3);

        for (int i = 0; i < 30; i++) {
            executorService.execute(new Task(i));
        }

        executorService.shutdown();

শুরু করার জন্য, মূল পদ্ধতিতে, আমরা একটি ExecutorService তৈরি করি এবং 30টি কার্য সম্পাদনের জন্য জমা দিই।

পুল-1-থ্রেড-2 থ্রেডে প্রসেসড ইউজার রিকোয়েস্ট #1
পুল-1-থ্রেড-1 থ্রেডে
প্রসেসড ইউজার রিকোয়েস্ট #0 পুল-1-থ্রেড-3 থ্রেডে ইউজার রিকোয়েস্ট #2
প্রসেসড ইউজার রিকোয়েস্ট #5 পুল-এ 1-থ্রেড-3 থ্রেড
পুল-1-থ্রেড-2 থ্রেডে প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #3
পুল-1-থ্রেড-1 থ্রেডে প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #4 পুল-1
-থ্রেড-1 থ্রেডে প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #8
প্রক্রিয়াকৃত ব্যবহারকারী পুল-1-থ্রেড-3 থ্রেডে অনুরোধ #6 পুল
-1-থ্রেড-2 থ্রেডে প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #7 পুল-1
-থ্রেড-3 থ্রেডে প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #10 পুল
-1-তে প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #9 থ্রেড-1 থ্রেড
পুল-1-থ্রেড-2 থ্রেডে প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #11
পুল-1-থ্রেড-3 থ্রেডে প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #12
পুল-1-থ্রেড-2 থ্রেডে প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #14 পুল
-1-থ্রেড-1 থ্রেডে
প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #13 পুল-1-থ্রেড-3 থ্রেডে প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #15 পুল-এ
প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #16 1-থ্রেড-2 থ্রেড
পুল-1-থ্রেড-1 থ্রেডে প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #17
পুল-1-থ্রেড-3 থ্রেডে
প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #18 পুল-1-থ্রেড-2 থ্রেডে প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #19
প্রক্রিয়াকৃত ব্যবহারকারী অনুরোধ #20 পুল-1-থ্রেড-1 থ্রেডে
প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #21 পুল-1-থ্রেড-3 থ্রেডে
প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #22 পুল-1-থ্রেড-2 থ্রেডে
প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #23 পুল-1-তে থ্রেড-1 থ্রেড
পুল-1-থ্রেড-2 থ্রেডে প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #25
পুল-1-থ্রেড-3 থ্রেডে প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #24
পুল-1-থ্রেড-1 থ্রেডে প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #26 পুল
-1-থ্রেড-2 থ্রেডে
প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #27 পুল-1-থ্রেড-3 থ্রেডে প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #28 পুল-এ
প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #29 1-থ্রেড-1 থ্রেড

কনসোল আউটপুট আমাদের দেখায় যে পূর্বের টাস্কটি প্রকাশ করার পরে বিভিন্ন থ্রেডে কাজগুলি কীভাবে কার্যকর করা হয়।

এখন আমরা কাজের সংখ্যা বাড়িয়ে 100 করব, এবং 100টি টাস্ক জমা দেওয়ার পরে, আমরা awaitTermination(11, SECONDS) পদ্ধতিতে কল করব। আমরা আর্গুমেন্ট হিসাবে একটি সংখ্যা এবং সময় ইউনিট পাস. এই পদ্ধতিটি 11 সেকেন্ডের জন্য মূল থ্রেডটিকে ব্লক করবে। তারপরে আমরা সমস্ত কাজ সম্পূর্ণ হওয়ার জন্য অপেক্ষা না করেই এক্সিকিউটরসার্ভিসকে বন্ধ করতে বাধ্য করতে shutdownNow() কল করব।

ExecutorService executorService = Executors.newFixedThreadPool(3);

        for (int i = 0; i < 100; i++) {
            executorService.execute(new Task(i));
        }

        executorService.awaitTermination(11, SECONDS);

        executorService.shutdownNow();
        System.out.println(executorService);

শেষে, আমরা executorService- এর অবস্থা সম্পর্কে তথ্য প্রদর্শন করব ।

আমরা যে কনসোল আউটপুটটি পাই তা এখানে:

পুল-1-থ্রেড-1 থ্রেডে প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #
0 পুল-1-থ্রেড-3 থ্রেডে
প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #2 পুল-1-থ্রেড-2 থ্রেডে প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #1 পুল-
এ প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #4 1-থ্রেড-3 থ্রেড
পুল-1-থ্রেড-2 থ্রেডে প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #5
পুল-1-থ্রেড-1 থ্রেডে
প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #3 পুল-1-থ্রেড-3 থ্রেডে প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #6
প্রক্রিয়াকৃত ব্যবহারকারী অনুরোধ #7 পুল-1-থ্রেড-2 থ্রেডে
প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #8 পুল-1-থ্রেড-1 থ্রেডে
প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #9 পুল-1-থ্রেড-3 থ্রেডে
প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #11 পুল-1-তে থ্রেড-1 থ্রেড
পুল-1-থ্রেড-2 থ্রেডে প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #10
পুল-1-থ্রেড-1 থ্রেডে প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #13
পুল-1-থ্রেড-2 থ্রেডে প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #14
পুল-1-থ্রেড-3 থ্রেডে প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #12
java.util.concurrent.ThreadPoolExecutor@452b3a41[শাট ডাউন, পুলের আকার = 3, সক্রিয় থ্রেড = 3 , সারিবদ্ধ কাজগুলি = 0, সম্পন্ন করা কাজগুলি = 15]
পুল-1-থ্রেড-3 থ্রেডে প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #17 পুল-
1-থ্রেড-1 থ্রেডে প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #15 পুল-1
-থ্রেডে প্রক্রিয়াকৃত ব্যবহারকারীর অনুরোধ #16 -2 থ্রেড

এটি 3টি বাধাপ্রাপ্ত ব্যতিক্রম দ্বারা অনুসরণ করা হয়েছে , 3টি সক্রিয় কাজ থেকে ঘুমের পদ্ধতি দ্বারা নিক্ষিপ্ত৷

আমরা দেখতে পাচ্ছি যে যখন প্রোগ্রামটি শেষ হয়, তখন 15টি কাজ সম্পন্ন হয়, কিন্তু পুলটিতে এখনও 3টি সক্রিয় থ্রেড ছিল যা তাদের কাজগুলি সম্পাদন করা শেষ করেনি। এই তিনটি থ্রেডে interrupt () মেথড কল করা হয়, যার মানে টাস্কটি সম্পূর্ণ হবে, কিন্তু আমাদের ক্ষেত্রে, স্লিপ মেথড একটি InterruptedException নিক্ষেপ করে । আমরা আরও দেখি যে shutdownNow() মেথড কল করার পরে, টাস্ক কিউ সাফ হয়ে গেছে।

সুতরাং পুলে নির্দিষ্ট সংখ্যক থ্রেড সহ একটি ExecutorService ব্যবহার করার সময় , এটি কীভাবে কাজ করে তা মনে রাখতে ভুলবেন না। এই ধরনের একটি পরিচিত ধ্রুবক লোড সঙ্গে কাজ জন্য উপযুক্ত.

এখানে আরেকটি আকর্ষণীয় প্রশ্ন: আপনার যদি একটি একক থ্রেডের জন্য একটি নির্বাহক ব্যবহার করতে হয়, তাহলে আপনি কোন পদ্ধতিতে কল করবেন? newSingleThreadExecutor() বা newFixedThreadPool(1) ?

উভয় নির্বাহকের সমান আচরণ থাকবে। শুধুমাত্র পার্থক্য হল newSingleThreadExecutor() পদ্ধতিটি এমন একটি নির্বাহককে ফিরিয়ে দেবে যা পরবর্তীতে অতিরিক্ত থ্রেড ব্যবহার করার জন্য পুনরায় কনফিগার করা যাবে না।