থ্রেডপুলএক্সিকিউটারের জমা () পদ্ধতি ব্লকটি কীভাবে স্যাচুরেটেড হয়?


102

আমি এমন একটি তৈরি করতে চাই ThreadPoolExecutorযে এটি যখন সর্বোচ্চ আকারে পৌঁছে এবং সারি পূর্ণ হয়, নতুন কার্যগুলি যুক্ত করার চেষ্টা করার সময় submit()পদ্ধতিটি বাধা দেয় । এর জন্য কি আমার কোনও কাস্টম বাস্তবায়ন করা দরকার RejectedExecutionHandlerবা স্ট্যান্ডার্ড জাভা লাইব্রেরি ব্যবহার করে এটি করার কোনও বিদ্যমান উপায় আছে?




2
@ ব্যাকার আমি একমত নই এই প্রশ্নোত্তরটি আরও মূল্যবান দেখাচ্ছে (বয়স বাড়ার পাশাপাশি)
জেসনমারেচার

উত্তর:


47

আমি সন্ধান করেছি এমন সম্ভাব্য সমাধানগুলির মধ্যে একটি:

public class BoundedExecutor {
    private final Executor exec;
    private final Semaphore semaphore;

    public BoundedExecutor(Executor exec, int bound) {
        this.exec = exec;
        this.semaphore = new Semaphore(bound);
    }

    public void submitTask(final Runnable command)
            throws InterruptedException, RejectedExecutionException {
        semaphore.acquire();
        try {
            exec.execute(new Runnable() {
                public void run() {
                    try {
                        command.run();
                    } finally {
                        semaphore.release();
                    }
                }
            });
        } catch (RejectedExecutionException e) {
            semaphore.release();
            throw e;
        }
    }
}

অন্য কোন সমাধান আছে? আমি এর উপর নির্ভর করে কিছু পছন্দ করবো RejectedExecutionHandlerযেহেতু এ জাতীয় পরিস্থিতিগুলি পরিচালনা করার জন্য এটি একটি আদর্শ উপায় বলে মনে হয়।


2
শেষ দফায় যখন সেমফোরটি প্রকাশিত হয় এবং সেমফোরটি অধিগ্রহণ করা হয় তখন বিন্দুটির মধ্যে এখানে কি রেসের শর্ত রয়েছে?
ভলনি

2
উপরে উল্লিখিত হিসাবে, এই প্রয়োগটি ত্রুটিযুক্ত কারণ টাস্কটি সম্পন্ন হওয়ার আগেই সেমফোরটি প্রকাশিত হয়েছিল। পদ্ধতি java.util.concurrent.ThreadPoolExecutor # afterExecute (চলমান, নিক্ষেপযোগ্য)
ফেলিক্স

2
@ ফেলিক্সএম: java.util.concurrent.ThreadPoolExecutor # afterExecute (চলমান, নিক্ষেপযোগ্য) সমস্যার সমাধান করবে না কারণ jaa.util.concurrent.ThreadPoolExecutor # रन ওয়ার্কার (ওয়ার্কার ডাব্লু) এর আগে টাস্ক.আরুন () এর পরপরই এক্সেকিউট বলা হয় before সারি থেকে পরবর্তী উপাদানটি গ্রহণ করা (ওপেনড্যাডকের 1.7.0.6 এর উত্স কোডের দিকে তাকানো)।
জান

1
এই উত্তরটি ব্রায়ান
গয়েটসের

11
এই উত্তরটি সম্পূর্ণ সঠিক নয়, মন্তব্যগুলিও। কোডের এই টুকরা প্রকৃতপক্ষে প্র্যাকটিস জাভা concurrency থেকে আসে, এবং এটি সঠিক কিনা আপনি একাউন্টে প্রসঙ্গটা নিতে । বইটি পরিষ্কারভাবে বলেছে, আক্ষরিক: "এই ধরণের পদ্ধতির জন্য, একটি আনবাউন্ডেড সারি (...) ব্যবহার করুন এবং পুলের আকারের সমতুল্য সমুদ্রতীরের উপরের সীমাটি সেট করুন এবং আপনি অনুমতি দিতে চান এমন সারিবদ্ধ কাজের সংখ্যা" " সীমাহীন কাতারে, কার্যগুলি কখনই প্রত্যাখ্যান করা হবে না, সুতরাং ব্যতিক্রমটির পুনর্বিবেচনা সম্পূর্ণ অকেজো! কোনটি, এছাড়াও কারণে আমি বিশ্বাস করি, throw e;হয় না গ্রন্থে। জেসিপি ঠিক আছে!
টিমমোস

30

আপনি থ্রেডপুলএক্সিকিউটর এবং একটি ব্লকিংকুই ব্যবহার করতে পারেন:

public class ImageManager {
    BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<Runnable>(blockQueueSize);
    RejectedExecutionHandler rejectedExecutionHandler = new ThreadPoolExecutor.CallerRunsPolicy();
    private ExecutorService executorService =  new ThreadPoolExecutor(numOfThread, numOfThread, 
        0L, TimeUnit.MILLISECONDS, blockingQueue, rejectedExecutionHandler);

    private int downloadThumbnail(String fileListPath){
        executorService.submit(new yourRunnable());
    }
}

আমি কেবল এটি বলতে চাই যে এটি কার্যকরভাবে কার্যকর করার জন্য একটি অত্যন্ত দ্রুত এবং সহজ সমাধান যা খুব ভালভাবে কাজ করেছিল!
ইভান

58
এটি জমা দেওয়ার থ্রেডে প্রত্যাখ্যাত কাজগুলি চালায়। যা কার্যকরীভাবে ওপি এর প্রয়োজনীয়তা পূরণ করে না।
অনুমান

4
এটি কাতারে রাখার জন্য ব্লকিং থ্রেডের পরিবর্তে "কলিং থ্রেডে" টাস্কটি চালায় যা এর কিছু বিরূপ প্রভাব ফেলতে পারে, যেমন একাধিক থ্রেড এটিকে কল করে, "সারি আকারের" বেশি কাজ চলবে এবং যদি টাস্কটি প্রত্যাশার চেয়ে বেশি সময় নেয়, আপনার "উত্পাদক" থ্রেড নির্বাহককে ব্যস্ত রাখতে পারে না। কিন্তু এখানে দুর্দান্ত কাজ!
রজারডপ্যাক 22

4
ডাউনভোটেড: টিপিইটি স্যাচুরেট হওয়ার পরে এটি ব্লক করে না। এটি কেবল একটি বিকল্প নয়, কোনও সমাধান নয়।
টিমমোস

1
উত্সাহিত: এটি ওভারফ্লো স্বাদগুলি দেওয়ার জন্য ক্লায়েন্টের থ্রেডগুলি 'টিপিই'র নকশা' এবং 'প্রাকৃতিকভাবে ব্লক' করে তোলে fits এটি সর্বাধিক ব্যবহারের কেসগুলি আবরণ করা উচিত, তবে অবশ্যই এটি সমস্ত নয় এবং আপনার বুঝতে হবে হুডের নীচে কী চলছে।
মাইক 10

12

আপনার ব্যবহার করা উচিত CallerRunsPolicy, যা কলিং থ্রেডে প্রত্যাখ্যাত কাজটি সম্পাদন করে। এই কাজটি শেষ না হওয়া পর্যন্ত এটি নির্বাহকের কাছে কোনও নতুন কাজ জমা দিতে পারে না, যার পর্যায়ে কিছু ফ্রি পুল থ্রেড থাকবে বা প্রক্রিয়াটি পুনরাবৃত্তি করবে।

http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/ThreadPoolExecutor.CallerRunsPolicy.html

ডক্স থেকে:

প্রত্যাখাত কর্ম

এক্সিকিউটরটি বন্ধ হয়ে গেলে, এবং যখন এক্সিকিউটার সর্বাধিক থ্রেড এবং কাজের সারি উভয় সক্ষমতা উভয়ের জন্য সীমাবদ্ধ সীমা ব্যবহার করে, এবং স্যাচুরেটেড হয় তখন পদ্ধতি কার্যকর করাতে (java.lang.Runnable) জমা দেওয়া নতুন কাজগুলি প্রত্যাখ্যানিত হবে। উভয় ক্ষেত্রেই, সম্পাদন পদ্ধতিটি তার প্রত্যাখাত এক্সএকশিউশনহ্যান্ডলারের RejectedExecutionHandler.rejectedExecution (java.lang.Runnable, java.util.concurrent.ThreadPoolExecutor) পদ্ধতির অনুরোধ করে। চারটি পূর্বনির্ধারিত হ্যান্ডলার নীতি সরবরাহ করা হয়েছে:

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

এছাড়াও, কনস্ট্রাক্টরকে কল করার সময় অ্যারেব্লকিংকিউয়ের মতো সীমাবদ্ধ সারি ব্যবহার করা নিশ্চিত করুন ThreadPoolExecutor। অন্যথায়, কিছুই প্রত্যাখ্যান হবে না।

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

সম্পাদনা 2: ঠিক আছে, আমি দেখছি আপনি কী পাচ্ছেন। এটি সম্পর্কে কী: এটি অতিক্রম করে না তা beforeExecute()পরীক্ষা করার পদ্ধতিটি ওভাররাইড করুন এবং যদি এটি হয় তবে ঘুমিয়ে আবার চেষ্টা করুন?getActiveCount()getMaximumPoolSize()


3
আমি একযোগে সম্পাদিত কার্যগুলি কঠোরভাবে আবদ্ধ করতে চাই (এক্সিকিউটারের থ্রেডের সংখ্যা দ্বারা), এই কারণেই আমি কলার থ্রেডগুলিকে এই কাজগুলি সম্পাদন করতে অনুমতি দিতে পারি না।
ফিক্সপয়েন্ট 1

1
অ্যাবার্টপলিসির ফলে কলার থ্রেড একটি প্রত্যাখাত এক্সেক্সিউশনএক্সেপশন গ্রহণ করবে, যখন আমার এটির জন্য কেবল ব্লক করা দরকার।
ফিক্সপয়েন্ট 1

2
আমি অবরুদ্ধকরণের জন্য জিজ্ঞাসা করছি, ঘুম না ও পোলিং;)
ফিক্সপয়েন্ট

@ এডেনবেন: আপনি কি কলার রান্সপলিস বোঝাতে চাইছেন না ?
ব্যবহারকারী 359996

7
কলাররনপলিসির সমস্যাটি হ'ল যদি আপনার একটি একক থ্রেড প্রযোজক থাকেন তবে দীর্ঘসময় চলমান কোনও কাজ প্রত্যাখ্যান করতে দেখা গেলে আপনার প্রায়শই থ্রেড ব্যবহার করা হয় না (কারণ থ্রেড পুলে থাকা অন্যান্য কাজগুলি শেষ হয়ে গেলেও দীর্ঘকালীন টাস্কটি এখনও বাকি থাকে) চলমান)।
অ্যাডাম জেন্ট

6

হাইবারনেটের একটি রয়েছে BlockPolicyযা সহজ এবং আপনি যা চান তা করতে পারে:

দেখুন: এক্সিকিউটর্স.জভা

/**
 * A handler for rejected tasks that will have the caller block until
 * space is available.
 */
public static class BlockPolicy implements RejectedExecutionHandler {

    /**
     * Creates a <tt>BlockPolicy</tt>.
     */
    public BlockPolicy() { }

    /**
     * Puts the Runnable to the blocking queue, effectively blocking
     * the delegating thread until space is available.
     * @param r the runnable task requested to be executed
     * @param e the executor attempting to execute this task
     */
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        try {
            e.getQueue().put( r );
        }
        catch (InterruptedException e1) {
            log.error( "Work discarded, thread was interrupted while waiting for space to schedule: {}", r );
        }
    }
}

4
দ্বিতীয় চিন্তায়, এটি একটি খুব খারাপ ধারণা। আপনি এটি ব্যবহার করার পরামর্শ দিচ্ছি না। সঙ্গত
নাট মারে

এছাড়াও, এটি ওপির অনুরোধ অনুযায়ী "স্ট্যান্ডার্ড জাভা লাইব্রেরি" ব্যবহার করছে না। মুছে ফেলা?
ব্যবহারকারী 359996

1
ওহ, এতো কুৎসিত। মূলত এই দ্রবণটি টিপিইর অভ্যন্তরীণ সাথে হস্তক্ষেপ করে। ThreadPoolExecutorএমনকি জাভাদোক আক্ষরিক অর্থেই বলেছেন: "পদ্ধতি getQueue () নিরীক্ষণ এবং ডিবাগিংয়ের উদ্দেশ্যে কাজের কাতারে অ্যাক্সেসের অনুমতি দেয়। অন্য কোনও উদ্দেশ্যে এই পদ্ধতির ব্যবহার দৃ strongly়ভাবে নিরুৎসাহিত করা হয়।" এটি এমন একটি লাইব্রেরিতে পাওয়া যায় যা এত বেশি পরিচিত, এটি দেখতে অত্যন্ত দুঃখজনক।
টিমমাস

1
com.amazonaws.services.simplework વહે.ফ্লো. ওয়ার্কার.ব্লককলারপলসি একই রকম।
অ্যাড্রিয়ান বাকের

6

BoundedExecutorউত্তর থেকে উপরে উদ্ধৃত প্র্যাকটিস জাভা concurrency শুধুমাত্র সঠিকভাবে কাজ করে যদি আপনি নির্বাহক জন্য একটি সীমাবদ্ধ কিউ ব্যবহার করুন, অথবা সেমফোর্ আবদ্ধ কিউ আকারের তুলনায় কোন বেশী। সেমফোরটি পুলটিতে জমা দেওয়ার থ্রেড এবং থ্রেডগুলির মধ্যে ভাগ করা রাষ্ট্র, যাতে সারি আকার <আবদ্ধ <= (কাতারের আকার + পুলের আকার) এমনকি নির্বাহককে পরিপূর্ণ করা সম্ভব করে তোলে।

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

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


আমি নিশ্চিত নই যে কীভাবে জমা দেওয়ার আগে সারির দৈর্ঘ্য পরীক্ষা করা একাধিক টাস্ক প্রযোজকদের সাথে বহু-থ্রেড পরিবেশে কোনও প্রত্যাখ্যাত কাজগুলি গ্যারান্টি দেয়। এটি থ্রেড-নিরাপদ শোনায় না।
টিম

5

আমি জানি, এটি একটি হ্যাক, তবে আমার মতে এখানে প্রস্তাবিতদের মধ্যে সবচেয়ে ক্লিন হ্যাক ;-)

যেহেতু থ্রেডপুলএ্যাক্সিকিউটর "পুট" এর পরিবর্তে "অফার" ব্লক করা সারি ব্যবহার করে, অবরুদ্ধ সারির "অফার" এর আচরণকে ওভাররাইড করতে দেয়:

class BlockingQueueHack<T> extends ArrayBlockingQueue<T> {

    BlockingQueueHack(int size) {
        super(size);
    }

    public boolean offer(T task) {
        try {
            this.put(task);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return true;
    }
}

ThreadPoolExecutor tp = new ThreadPoolExecutor(1, 2, 1, TimeUnit.MINUTES, new BlockingQueueHack(5));

আমি এটি পরীক্ষা করেছি এবং এটি কাজ করে বলে মনে হচ্ছে। কিছু সময়সীমা নীতি বাস্তবায়ন পাঠকের অনুশীলন হিসাবে বাকি আছে।


এর ক্লিন আপ সংস্করণটির জন্য stackoverflow.com/a/4522411/2601671 দেখুন । আমি সম্মত, এটি করার সবচেয়ে পরিষ্কার উপায়।
ট্রেনটন

3

নিম্নলিখিত শ্রেণিটি একটি থ্রেডপুলএক্সিকিউটারের চারপাশে মোড়ক করে এবং ব্লক করতে একটি সেমফোর ব্যবহার করে তারপরে কাজের সারিটি পূর্ণ:

public final class BlockingExecutor { 

    private final Executor executor;
    private final Semaphore semaphore;

    public BlockingExecutor(int queueSize, int corePoolSize, int maxPoolSize, int keepAliveTime, TimeUnit unit, ThreadFactory factory) {
        BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
        this.executor = new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTime, unit, queue, factory);
        this.semaphore = new Semaphore(queueSize + maxPoolSize);
    }

    private void execImpl (final Runnable command) throws InterruptedException {
        semaphore.acquire();
        try {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        command.run();
                    } finally {
                        semaphore.release();
                    }
                }
            });
        } catch (RejectedExecutionException e) {
            // will never be thrown with an unbounded buffer (LinkedBlockingQueue)
            semaphore.release();
            throw e;
        }
    }

    public void execute (Runnable command) throws InterruptedException {
        execImpl(command);
    }
}

এই মোড়কের ক্লাসটি ব্রায়ান গয়েটসের অনুশীলনে জাভা কনকুরન્સી বইয়ে দেওয়া সমাধানের ভিত্তিতে। বইয়ের সমাধানটিতে কেবল দুটি কনস্ট্রাক্টর প্যারামিটার লাগে: Executorসেমফোরের জন্য একটি এবং একটি সীমা ব্যবহৃত। এটি ফিক্সপয়েন্টের দেওয়া উত্তরে দেখানো হয়েছে। এই পদ্ধতির সাথে একটি সমস্যা আছে: এটি এমন অবস্থায় যেতে পারে যেখানে পুলের থ্রেডগুলি ব্যস্ত থাকে, সারিটি পূর্ণ থাকে, তবে সেমফোর সবেমাত্র একটি অনুমতি প্রকাশ করেছে। (semaphore.release() শেষ অবধি) এই রাজ্যে, একটি নতুন টাস্ক সবেমাত্র প্রকাশিত পারমিটটি দখল করতে পারে, তবে টাস্ক সারি পূর্ণ হওয়ায় তা প্রত্যাখ্যান করা হয়। অবশ্যই এটি আপনি চান এমন কিছু নয়; আপনি এই ক্ষেত্রে অবরুদ্ধ করতে চান।

এই সমস্যার সমাধানের উদ্দেশ্যে, আমরা একটি ব্যবহার করা আবশ্যক সীমাবদ্ধ যেমন JCiP পরিষ্কারভাবে উল্লেখ, কিউ। ভার্চুয়াল সারি আকারের প্রভাব প্রদান করে সেমফোর একটি প্রহরী হিসাবে কাজ করে। এর পার্শ্ব প্রতিক্রিয়া রয়েছে যে ইউনিটটি maxPoolSize + virtualQueueSize + maxPoolSizeকার্যগুলি থাকতে পারে এটি সম্ভব । তা কেন? কারণ semaphore.release()শেষ অবধি। যদি সমস্ত পুল থ্রেড একই সময়ে এই বিবৃতিতে কল করে, তবে maxPoolSizeএকই সংখ্যার কাজগুলিকে ইউনিটে প্রবেশের অনুমতি দিয়ে অনুমতিগুলি মুক্তি দেওয়া হয়। যদি আমরা একটি সীমাবদ্ধ সারি ব্যবহার করতাম তবে এটি পুরোপুরি পূর্ণ হবে, ফলস্বরূপ প্রত্যাখ্যানিত কোনও কার্য। এখন, কারণ আমরা জানি যে এটি কেবল তখনই ঘটে যখন পুলের থ্রেডটি প্রায় শেষ হয়ে যায়, এটি কোনও সমস্যা নয়। আমরা জানি যে পুলের থ্রেডটি ব্লক করবে না, সুতরাং শীঘ্রই একটি কাজ নেওয়া হবে।

আপনি যদিও সীমাবদ্ধ সারি ব্যবহার করতে সক্ষম হন। এটির আকার সমান হয় তা নিশ্চিত করুন virtualQueueSize + maxPoolSize। বৃহত্তর আকারগুলি অকেজো, সেম্যাফোর আরও আইটেমগুলিতে প্রবেশ করতে বাধা দেবে mal আকার হ্রাস হওয়ায় কাজের প্রত্যাখাত হওয়ার সুযোগ বাড়ে। উদাহরণস্বরূপ, বলুন আপনি সর্বাধিকপুলসাইজ = 2 এবং ভার্চুয়ালকিউসাইজ = 5 সহ একটি সীমাবদ্ধ নির্বাহক চান। তারপরে 5 + 2 = 7 পারমিট এবং 5 + 2 = 7 এর আসল কাতারের আকারের সাথে একটি সেমফোর নিন। ইউনিটে থাকা কার্যগুলির প্রকৃত সংখ্যাটি হ'ল 2 + ​​5 + 2 = 9। যখন নির্বাহক পূর্ণ হয়ে থাকে (সারিতে 5 টি কাজ, থ্রেড পুলে 2, সুতরাং 0 পারমিট উপলব্ধ থাকে) এবং সমস্ত পুল থ্রেডগুলি তাদের পারমিট প্রকাশ করে, ঠিক তখনই 2 টি অনুমতি নিয়ে আসা কার্যগুলি গ্রহণ করা যেতে পারে।

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


2

আপনি এটির মতো একটি কাস্টম প্রত্যাখাত এক্সেকিউশনহ্যান্ডলার ব্যবহার করতে পারেন

ThreadPoolExecutor tp= new ThreadPoolExecutor(core_size, // core size
                max_handlers, // max size 
                timeout_in_seconds, // idle timeout 
                TimeUnit.SECONDS, queue, new RejectedExecutionHandler() {
                    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                        // This will block if the queue is full
                        try {
                            executor.getQueue().put(r);
                        } catch (InterruptedException e) {
                            System.err.println(e.getMessage());
                        }

                    }
                });

1
GetQueue () এর জন্য দস্তাবেজগুলি স্পষ্টভাবে উল্লেখ করেছে যে টাস্ক কাতারে অ্যাক্সেস মূলত ডিবাগিং এবং পর্যবেক্ষণের জন্য।
চাদী

0

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

আমি বিশ্বাস করি এটি আপনার সন্ধানের জন্য ব্লক করা আচরণ পাবে। একটি প্রত্যাখ্যান হ্যান্ডলার কখনই বিলের সাথে খাপ খায় না, কারণ এটি নির্দেশ করে যে নির্বাহক কার্য সম্পাদন করতে পারবেন না। আমি যা কল্পনা করতে পারি তা হ্যান্ডলারের মধ্যে আপনি 'ব্যস্ত ওয়েটিং' এর কিছু ফর্ম পাবেন। এটি আপনি যা চান তা নয়, আপনি নির্বাহকের পক্ষে একটি সারি চাই যা কলকারীকে ব্লক করে ...


2
ThreadPoolExecutorofferসারিতে কর্ম যুক্ত করতে পদ্ধতি ব্যবহার করে । যদি আমি এমন একটি কাস্টম তৈরি করি BlockingQueueযা অবরুদ্ধ হয় offerতবে তা BlockingQueueচুক্তি ভঙ্গ করবে ।
ফিক্সপয়েন্ট

@ শুশপঞ্চিক, এটি ব্লকিংকুইজ চুক্তি ভঙ্গ করবে। তাতে কি? আপনি যদি এত আগ্রহী হন তবে আপনি কেবল জমা দেওয়ার সময় () এটি স্পষ্টভাবে আচরণটি সক্ষম করতে পারবেন (এটি একটি থ্রেডলোকাল নেবে)
ব্যাস্তসেস

এই বিকল্পটি বানান যা অন্য প্রশ্নের উত্তর দেখুন ।
রবার্ট টুপেলো-শ্নেক

(ব্লকিং সংস্করণ) ThreadPoolExecutorব্যবহার করার জন্য offerএবং প্রয়োগ করার জন্য কেন কোনও কারণ রয়েছে put? এছাড়াও, যদি ক্লায়েন্ট কোডটি কোনটি কখন ব্যবহার করতে হবে তা জানার কোনও উপায় থাকলে, কাস্টম সমাধানগুলি হ্যান্ড-রোল করার চেষ্টা করে প্রচুর লোকেরা মুক্তি পেয়ে যেত
asgs

0

@ ফিক্সপয়েন্ট সমাধান সহ সমস্যাগুলি এড়াতে। লিনিংএ্যাক্সিকিউটর সার্ভিস ব্যবহার করে কেউ ফিউচারক্যালব্যাকের অভ্যন্তরে onSuccess এবং onFailure এ semaphore প্রকাশ করতে পারে।


সাধারণ Runnableপদ্ধতিতে কর্মী পরিষ্কারের আগে সেই পদ্ধতিগুলিকে এখনও ডেকে আনা যেমন ঠিক তেমনই অন্তর্নিহিত সমস্যা রয়েছে ThreadPoolExecutor। এটি এখনও আপনার প্রত্যাখ্যান ব্যতিক্রমগুলি পরিচালনা করতে হবে।
অ্যাডাম জেন্ট 16

0

সম্প্রতি আমি এই প্রশ্নটি একই সমস্যা পেয়েছি। ওপি তেমন স্পষ্টভাবে বলে না, তবে আমরা RejectedExecutionHandlerসাবমিটারের থ্রেডে কোন কাজটি কার্যকর করে এমনটি ব্যবহার করতে চাই না , কারণ এই কাজটি দীর্ঘকাল চলমান থাকলে শ্রমিকের থ্রেডকে কম ব্যবহার করবে will

সমস্ত উত্তর এবং মন্তব্য পড়া, বিশেষত সেমফোরের সাথে ত্রুটিযুক্ত সমাধান বা ব্যবহার afterExecuteকরে থ্রেডপুলএক্সেকিউটারের কোডটি ঘনিষ্ঠভাবে দেখেছিলাম কোনও উপায় আছে কিনা তা দেখার জন্য। আমি দেখে বিস্মিত হয়েছি যে এখানে (মন্তব্য করা) কোডের 2000 টিরও বেশি লাইন রয়েছে, যার মধ্যে কিছু আমাকে চঞ্চল করে তোলে । আমার কাছে বরং সাধারণ প্রয়োজনের কারণে --- একজন প্রযোজক, বেশ কয়েকটি ভোক্তা, যখন কোনও গ্রাহক কাজ নিতে না পারে তখন নির্মাতাকে ব্লক করতে দিন --- আমি নিজের সমাধানটি রোল করার সিদ্ধান্ত নিয়েছি। এটি একটি ExecutorServiceনয়, কেবল একটি Executor। এবং এটি কাজের লোডের সাথে থ্রেডের সংখ্যাকে অভিযোজিত করে না, তবে কেবলমাত্র থ্রেডের একটি নির্দিষ্ট সংখ্যক ধারণ করে যা আমার প্রয়োজনীয়তার সাথেও ফিট করে। কোডটি এখানে। এটি সম্পর্কে নির্দ্বিধায় নির্দ্বিধায় :-)

package x;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.SynchronousQueue;

/**
 * distributes {@code Runnable}s to a fixed number of threads. To keep the
 * code lean, this is not an {@code ExecutorService}. In particular there is
 * only very simple support to shut this executor down.
 */
public class ParallelExecutor implements Executor {
  // other bounded queues work as well and are useful to buffer peak loads
  private final BlockingQueue<Runnable> workQueue =
      new SynchronousQueue<Runnable>();
  private final Thread[] threads;

  /*+**********************************************************************/
  /**
   * creates the requested number of threads and starts them to wait for
   * incoming work
   */
  public ParallelExecutor(int numThreads) {
    this.threads = new Thread[numThreads];
    for(int i=0; i<numThreads; i++) {
      // could reuse the same Runner all over, but keep it simple
      Thread t = new Thread(new Runner());
      this.threads[i] = t;
      t.start();
    }
  }
  /*+**********************************************************************/
  /**
   * returns immediately without waiting for the task to be finished, but may
   * block if all worker threads are busy.
   * 
   * @throws RejectedExecutionException if we got interrupted while waiting
   *         for a free worker
   */
  @Override
  public void execute(Runnable task)  {
    try {
      workQueue.put(task);
    } catch (InterruptedException e) {
      Thread.currentThread().interrupt();
      throw new RejectedExecutionException("interrupt while waiting for a free "
          + "worker.", e);
    }
  }
  /*+**********************************************************************/
  /**
   * Interrupts all workers and joins them. Tasks susceptible to an interrupt
   * will preempt their work. Blocks until the last thread surrendered.
   */
  public void interruptAndJoinAll() throws InterruptedException {
    for(Thread t : threads) {
      t.interrupt();
    }
    for(Thread t : threads) {
      t.join();
    }
  }
  /*+**********************************************************************/
  private final class Runner implements Runnable {
    @Override
    public void run() {
      while (!Thread.currentThread().isInterrupted()) {
        Runnable task;
        try {
          task = workQueue.take();
        } catch (InterruptedException e) {
          // canonical handling despite exiting right away
          Thread.currentThread().interrupt(); 
          return;
        }
        try {
          task.run();
        } catch (RuntimeException e) {
          // production code to use a logging framework
          e.printStackTrace();
        }
      }
    }
  }
}

0

আমি বিশ্বাস করি এর java.util.concurrent.Semaphoreআচরণটি ব্যবহার করে এবং প্রতিনিধিত্ব করে এই সমস্যা সমাধানের বেশ মার্জিত উপায় আছে Executor.newFixedThreadPool। নতুন এক্সিকিউটার পরিষেবা কেবল তখনই নতুন কার্য সম্পাদন করবে যখন সেখানে কোনও থ্রেড থাকবে। ব্লকিং থিম সংখ্যার সমান পারমিটের সংখ্যা সহ সেমফোর দ্বারা পরিচালনা করা হয়। কোনও কাজ শেষ হয়ে গেলে এটি অনুমতি দেয়।

public class FixedThreadBlockingExecutorService extends AbstractExecutorService {

private final ExecutorService executor;
private final Semaphore blockExecution;

public FixedThreadBlockingExecutorService(int nTreads) {
    this.executor = Executors.newFixedThreadPool(nTreads);
    blockExecution = new Semaphore(nTreads);
}

@Override
public void shutdown() {
    executor.shutdown();
}

@Override
public List<Runnable> shutdownNow() {
    return executor.shutdownNow();
}

@Override
public boolean isShutdown() {
    return executor.isShutdown();
}

@Override
public boolean isTerminated() {
    return executor.isTerminated();
}

@Override
public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
    return executor.awaitTermination(timeout, unit);
}

@Override
public void execute(Runnable command) {
    blockExecution.acquireUninterruptibly();
    executor.execute(() -> {
        try {
            command.run();
        } finally {
            blockExecution.release();
        }
    });
}

আমি অনুশীলনে জাভা কনকুরানসিতে বর্ণিত বাউন্ডেডএকসিকিউটর বাস্তবায়ন করেছি এবং বুঝতে পেরেছি যে অর্ডার অনুরোধগুলি করা হচ্ছে সেমফোর পারমিটগুলি নিশ্চিত করা হয় তা নিশ্চিত করার জন্য সেম্যাফোরটি যথার্থতার সাথে নির্ধারণ করা উচিত ness পড়ুন docs.oracle.com/javase/7/docs/api/java/util/concurrent/... । বিশদ জন্য
প্রহ্লাদ দেশপাণ্ডে

0

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

ইউজারথ্রেডপুলএক্সিকিউটর (ব্লকিং সারি (প্রতি ক্লায়েন্ট) + থ্রেডপুল (সমস্ত ক্লায়েন্টের মধ্যে ভাগ করা)

দেখুন: https://github.com/d4rxh4wx/UserThreadPoolExecutor

প্রতিটি ইউজারথ্রেডপুলএক্সিকিউটরকে একটি ভাগ করা থ্রেডপুলএক্সেক্টর থেকে সর্বোচ্চ সংখ্যক থ্রেড দেওয়া হয়

প্রতিটি ইউজারথ্রেডপুলএক্সিকিউটর:

  • ভাগ করা থ্রেড পুল নির্বাহকের কাছে যদি কোনও কোটা না পৌঁছায় তবে তা জমা দিন। যদি এর কোটা পৌঁছে যায়, কাজটি সারিবদ্ধ (সিপিইউর অপেক্ষায় থাকা অবরুদ্ধ অবরোধ)। তার জমা দেওয়া কোনও কাজ শেষ হয়ে গেলে, কোটা হ্রাস করা হবে, অন্য একটি কাজ থ্রেডপুলএক্সেকিউটারে জমা দেওয়ার জন্য অপেক্ষা করা হচ্ছে
  • বাকি কাজ শেষ হওয়ার জন্য অপেক্ষা করুন

0

আমি এই প্রত্যাখ্যান নীতিটি স্থিতিস্থাপক অনুসন্ধান ক্লায়েন্টে পেয়েছি। এটি ব্লক করা সারিটিতে কলার থ্রেড অবরোধ করে। নীচে কোড-

 static class ForceQueuePolicy implements XRejectedExecutionHandler 
 {
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) 
        {
            try 
            {
                executor.getQueue().put(r);
            } 
            catch (InterruptedException e) 
            {
                //should never happen since we never wait
                throw new EsRejectedExecutionException(e);
            }
        }

        @Override
        public long rejected() 
        {
            return 0;
        }
}

0

আমার সম্প্রতি কিছু অনুরূপ অর্জন করার প্রয়োজন ছিল, তবে এ ScheduledExecutorService

আমাকেও নিশ্চিত করতে হয়েছিল যে পদ্ধতিটিতে যে বিলম্ব হচ্ছে তা আমি পরিচালনা করেছি এবং কলারের প্রত্যাশা হওয়ার সাথে সাথে কার্য সম্পাদন করার জন্য কার্যটি জমা দেওয়া হয়েছে কিনা তা নিশ্চিত করতে হয়েছিল বা কেবল একটি নিক্ষেপ করতে ব্যর্থ হয়েছে RejectedExecutionException

ScheduledThreadPoolExecutorঅভ্যন্তরীণভাবে একটি টাস্ক কার্যকর করতে বা জমা দেওয়ার জন্য অন্যান্য পদ্ধতিগুলি কল রয়েছে #scheduleযা এখনও পরিবর্তিত পদ্ধতিগুলিকে ওভাররাইড করে inv

import java.util.concurrent.*;

public class BlockingScheduler extends ScheduledThreadPoolExecutor {
    private final Semaphore maxQueueSize;

    public BlockingScheduler(int corePoolSize,
                             ThreadFactory threadFactory,
                             int maxQueueSize) {
        super(corePoolSize, threadFactory, new AbortPolicy());
        this.maxQueueSize = new Semaphore(maxQueueSize);
    }

    @Override
    public ScheduledFuture<?> schedule(Runnable command,
                                       long delay,
                                       TimeUnit unit) {
        final long newDelayInMs = beforeSchedule(command, unit.toMillis(delay));
        return super.schedule(command, newDelayInMs, TimeUnit.MILLISECONDS);
    }

    @Override
    public <V> ScheduledFuture<V> schedule(Callable<V> callable,
                                           long delay,
                                           TimeUnit unit) {
        final long newDelayInMs = beforeSchedule(callable, unit.toMillis(delay));
        return super.schedule(callable, newDelayInMs, TimeUnit.MILLISECONDS);
    }

    @Override
    public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                                  long initialDelay,
                                                  long period,
                                                  TimeUnit unit) {
        final long newDelayInMs = beforeSchedule(command, unit.toMillis(initialDelay));
        return super.scheduleAtFixedRate(command, newDelayInMs, unit.toMillis(period), TimeUnit.MILLISECONDS);
    }

    @Override
    public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
                                                     long initialDelay,
                                                     long period,
                                                     TimeUnit unit) {
        final long newDelayInMs = beforeSchedule(command, unit.toMillis(initialDelay));
        return super.scheduleWithFixedDelay(command, newDelayInMs, unit.toMillis(period), TimeUnit.MILLISECONDS);
    }

    @Override
    protected void afterExecute(Runnable runnable, Throwable t) {
        super.afterExecute(runnable, t);
        try {
            if (t == null && runnable instanceof Future<?>) {
                try {
                    ((Future<?>) runnable).get();
                } catch (CancellationException | ExecutionException e) {
                    t = e;
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt(); // ignore/reset
                }
            }
            if (t != null) {
                System.err.println(t);
            }
        } finally {
            releaseQueueUsage();
        }
    }

    private long beforeSchedule(Runnable runnable, long delay) {
        try {
            return getQueuePermitAndModifiedDelay(delay);
        } catch (InterruptedException e) {
            getRejectedExecutionHandler().rejectedExecution(runnable, this);
            return 0;
        }
    }

    private long beforeSchedule(Callable callable, long delay) {
        try {
            return getQueuePermitAndModifiedDelay(delay);
        } catch (InterruptedException e) {
            getRejectedExecutionHandler().rejectedExecution(new FutureTask(callable), this);
            return 0;
        }
    }

    private long getQueuePermitAndModifiedDelay(long delay) throws InterruptedException {
        final long beforeAcquireTimeStamp = System.currentTimeMillis();
        maxQueueSize.tryAcquire(delay, TimeUnit.MILLISECONDS);
        final long afterAcquireTimeStamp = System.currentTimeMillis();
        return afterAcquireTimeStamp - beforeAcquireTimeStamp;
    }

    private void releaseQueueUsage() {
        maxQueueSize.release();
    }
}

আমার কাছে কোডটি এখানে রয়েছে, যে কোনও প্রতিক্রিয়ার প্রশংসা করব। https://github.com/AmitabhAwasthi/BlockingScheduler


এই উত্তরটি সম্পূর্ণরূপে বাহ্যিক লিঙ্কগুলির সামগ্রীতে নির্ভর করে। এগুলি যদি কখনও অবৈধ হয়ে যায় তবে আপনার উত্তরটি অকেজো হবে। সুতরাং দয়া করে আপনার উত্তরটি সম্পাদনা করুন এবং সেখানে কী পাওয়া যাবে তার কমপক্ষে একটি সংক্ষিপ্তসার যুক্ত করুন। ধন্যবাদ!
ফ্যাবিও মনিকাকে পুনরায়

@ ফ্যাবিও: দেখানোর জন্য ধন্যবাদ আমি কোডটি এখানে যুক্ত করেছি যাতে এটি এখন পাঠকদের আরও জ্ঞাত করে তোলে। আপনার মন্তব্যের প্রশংসা করুন :)
দেব অমিতাভ

0

এখানে সমাধানটি যা সত্যিই ভালভাবে কাজ করছে বলে মনে হচ্ছে। একে নোটিফাইং ব্লকিং ট্র্যাডপুলএক্সেকিউটার বলা হয় ।

ডেমো প্রোগ্রাম।

সম্পাদনা করুন: এই কোডটিতে একটি সমস্যা আছে , অপেক্ষা () পদ্ধতিটি বাগি। কল ডাউন শাটডাউন () + অপেক্ষারতীক্ষা () মনে হচ্ছে ঠিক আছে।


0

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

আমি একটি কাস্টম রিজেক্টেড এক্সেকিউশনহ্যান্ডলার ব্যবহার করে এই সমস্যাটি সমাধান করেছি, যা কলিং থ্রেডটি কেবল অল্প সময়ের জন্য অবরুদ্ধ করে এবং তারপরে আবার টাস্কটি জমা দেওয়ার চেষ্টা করে:

public class BlockWhenQueueFull implements RejectedExecutionHandler {

    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {

        // The pool is full. Wait, then try again.
        try {
            long waitMs = 250;
            Thread.sleep(waitMs);
        } catch (InterruptedException interruptedException) {}

        executor.execute(r);
    }
}

এই শ্রেণিটি কেবল থ্রেড-পুল নির্বাহকে অন্য যেহেতু প্রত্যাশিত এক্সেকটিনহ্যান্ডলার হিসাবে ব্যবহার করা যেতে পারে, উদাহরণস্বরূপ:

executorPool = new ThreadPoolExecutor(1, 1, 10,
                                      TimeUnit.SECONDS, new SynchronousQueue<Runnable>(),
                                      new BlockWhenQueueFull());

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

তবুও, আমি ব্যক্তিগতভাবে এই পদ্ধতিটি পছন্দ করি। এটি কমপ্যাক্ট, বোঝা সহজ এবং ভালভাবে কাজ করে।


1
আপনি যেমন নিজেকে বলেছেন: এটি একটি স্ট্যাকওভারফ্লো তৈরি করতে পারে। আমি প্রডাকশন কোড পেতে চাই এমন কিছু নয়।
হ্যারাল্ড

প্রত্যেকের নিজের সিদ্ধান্ত নেওয়া উচিত। আমার কাজের চাপের জন্য, এটি কোনও সমস্যা নয়। স্ট্যাকটি ফুঁ দেওয়ার জন্য প্রয়োজনীয় সময়গুলির পরিবর্তে কার্যগুলি সেকেন্ডে চলে। তদুপরি, একইভাবে ভার্চুয়ালি কোনও পুনরাবৃত্তির অ্যালগরিদমের জন্য বলা যেতে পারে। উত্পাদনে কোনও পুনরাবৃত্তির অ্যালগরিদম ব্যবহার করার কোনও কারণ কি এটি?
টিঙ্কারট্যাঙ্ক
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.