কেন আপনি 1 থ্রেড জন্য একটি ExecutorService প্রয়োজন হতে পারে?

আপনি Executors.newSingleThreadExecutor পদ্ধতি ব্যবহার করতে পারেন একটি পুল সহ একটি ExecutorService তৈরি করতে যাতে একটি একক থ্রেড রয়েছে। পুলের যুক্তি নিম্নরূপ:

  • পরিষেবাটি একবারে শুধুমাত্র একটি কাজ সম্পাদন করে।
  • যদি আমরা সম্পাদনের জন্য N টাস্ক জমা দিই, তাহলে সমস্ত N টাস্ক একের পর এক একক থ্রেড দ্বারা কার্যকর করা হবে।
  • থ্রেডটি বাধাগ্রস্ত হলে, অবশিষ্ট কাজগুলি চালানোর জন্য একটি নতুন থ্রেড তৈরি করা হবে।

আসুন এমন একটি পরিস্থিতি কল্পনা করি যেখানে আমাদের প্রোগ্রামটির নিম্নলিখিত কার্যকারিতা প্রয়োজন:

আমাদের 30 সেকেন্ডের মধ্যে ব্যবহারকারীর অনুরোধগুলি প্রক্রিয়া করতে হবে, তবে প্রতি ইউনিটে একটির বেশি অনুরোধ করা যাবে না।

আমরা একটি ব্যবহারকারীর অনুরোধ প্রক্রিয়া করার জন্য একটি টাস্ক ক্লাস তৈরি করি:


class Task implements Runnable {
   private final int taskNumber;

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

   @Override
   public void run() {
       try {
           Thread.sleep(1000);
       } catch (InterruptedException ignored) {
       }
       System.out.printf("Processed request #%d on thread id=%d\\n", taskNumber, Thread.currentThread().getId());
   }
}
    

ক্লাস একটি আগত অনুরোধ প্রক্রিয়াকরণের আচরণের মডেল করে এবং এর সংখ্যা প্রদর্শন করে।

এর পরে, মূল পদ্ধতিতে, আমরা 1 থ্রেডের জন্য একটি ExecutorService তৈরি করি, যা আমরা ক্রমানুসারে আগত অনুরোধগুলি প্রক্রিয়া করতে ব্যবহার করব। যেহেতু টাস্ক শর্তাবলী "30 সেকেন্ডের মধ্যে" নির্ধারণ করে, আমরা 30-সেকেন্ড অপেক্ষা যোগ করি এবং তারপর জোরপূর্বক ExecutorService বন্ধ করি


public static void main(String[] args) throws InterruptedException {
   ExecutorService executorService = Executors.newSingleThreadExecutor();

   for (int i = 0; i < 1_000; i++) {
       executorService.execute(new Task(i));
   }
   executorService.awaitTermination(30, TimeUnit.SECONDS);
   executorService.shutdownNow();
}
    

প্রোগ্রামটি শুরু করার পরে, কনসোল অনুরোধ প্রক্রিয়াকরণ সম্পর্কে বার্তাগুলি প্রদর্শন করে:

থ্রেড আইডিতে প্রসেসড রিকোয়েস্ট #0=16
প্রসেসড রিকোয়েস্ট #1 থ্রেড আইডি=16
প্রসেসড রিকোয়েস্ট #2 থ্রেড আইডি=16

থ্রেড আইডি=16-এ প্রসেসড রিকোয়েস্ট #29

30 সেকেন্ডের জন্য অনুরোধগুলি প্রক্রিয়া করার পরে, executorService shutdownNow() পদ্ধতিতে কল করে , যা বর্তমান কাজটি বন্ধ করে দেয় (একটি কার্যকর করা হচ্ছে) এবং সমস্ত মুলতুবি কাজগুলি বাতিল করে। এর পরে, প্রোগ্রামটি সফলভাবে শেষ হয়।

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

এটি করার জন্য, যখন একটি কাজ চালানো হচ্ছে, আমরা আমাদের থ্রেডটি অনিরাপদ এবং অপ্রচলিত Thread.currentThread().stop() পদ্ধতি ব্যবহার করে বন্ধ করে দিই। আমরা এমন পরিস্থিতি অনুকরণ করতে ইচ্ছাকৃতভাবে এটি করছি যেখানে একটি কাজ থ্রেডটি বন্ধ করে দেয়।

আমরা টাস্ক ক্লাসে রান পদ্ধতি পরিবর্তন করব :


@Override
public void run() {
   try {
       Thread.sleep(1000);
   } catch (InterruptedException ignored) {
   }

   if (taskNumber == 5) {
       Thread.currentThread().stop();
   }

   System.out.printf("Processed request #%d on thread id=%d\\n", taskNumber, Thread.currentThread().getId());
}
    

আমরা টাস্ক #5 বাধা দেব।

চলুন দেখি টাস্ক #5 এর শেষে থ্রেড বাধা দিলে আউটপুটটি কেমন দেখায়:

থ্রেড আইডিতে প্রসেসড রিকোয়েস্ট #0=16
থ্রেড আইডিতে প্রসেসড রিকোয়েস্ট #1=16
প্রসেসড রিকোয়েস্ট #2 থ্রেড আইডি=16
প্রসেসড রিকোয়েস্ট #3 থ্রেড আইডি=16
থ্রেড আইডিতে প্রসেসড রিকোয়েস্ট #4=16
প্রসেসড রিকোয়েস্ট #6 থ্রেড আইডি=17
প্রসেসড রিকোয়েস্ট #7 থ্রেড আইডি=17

প্রসেসড রিকোয়েস্ট #29

আমরা দেখতে পাই যে টাস্ক 5 এর শেষে থ্রেডটি বাধাগ্রস্ত হওয়ার পরে, কাজগুলি একটি থ্রেডে কার্যকর করা শুরু করে যার শনাক্তকারী 17, যদিও সেগুলি আগে থ্রেডে কার্যকর করা হয়েছিল যার শনাক্তকারী 16। এবং কারণ আমাদের পুলটিতে একটি একক থ্রেড, এটি শুধুমাত্র একটি জিনিস বোঝাতে পারে: executorService একটি নতুন দিয়ে বন্ধ থ্রেড প্রতিস্থাপন করেছে এবং কাজগুলি চালানো অব্যাহত রেখেছে।

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

থ্রেড ফ্যাক্টরি

থ্রেড তৈরি এবং পুনরায় তৈরি করার কথা বলার সময়, আমরা উল্লেখ করা ছাড়া সাহায্য করতে পারি নাথ্রেড ফ্যাক্টরি.

থ্রেড ফ্যাক্টরিএকটি বস্তু যা চাহিদা অনুযায়ী নতুন থ্রেড তৈরি করে।

আমরা আমাদের নিজস্ব থ্রেড তৈরির কারখানা তৈরি করতে পারি এবং এর একটি উদাহরণ Executors.newSingleThreadExecutor(ThreadFactory threadFactory) পদ্ধতিতে পাঠাতে পারি।


ExecutorService executorService = Executors.newSingleThreadExecutor(new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "MyThread");
            }
        });
                    
আমরা একটি নতুন থ্রেড তৈরি করার পদ্ধতিটি ওভাররাইড করি, একটি থ্রেডের নাম কনস্ট্রাক্টরকে প্রেরণ করি।

ExecutorService executorService = Executors.newSingleThreadExecutor(new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                Thread thread = new Thread(r, "MyThread");
                thread.setPriority(Thread.MAX_PRIORITY);
                return thread;
            }
        });
                    
আমরা তৈরি থ্রেডের নাম এবং অগ্রাধিকার পরিবর্তন করেছি।

সুতরাং আমরা দেখতে পাচ্ছি যে আমাদের কাছে 2টি ওভারলোডেড Executors.newSingleThreadExecutor পদ্ধতি রয়েছে। একটি পরামিতি ছাড়া, এবং একটি ThreadFactory পরামিতি সহ একটি দ্বিতীয়৷

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