Java ThreadPoolExecutor should follow the sequence of submitted jobs

Viewed 63

I am a beginner with ThreadPoolExecutor and need your expert advice for below scenario

Here,I submit 30 jobs in a thread executor which should get executed in sequence.

ThreadPoolExecutor exeSer = new ThreadPoolExecutor(5, 10, 15, TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(20));

for(int i = 0; i<30; i++) {
    exeSer.submit(new MyRunnableService(i));
}

Current Behaviour

When I submit 30 jobs to execute, the executor created 5 threads for first 5 jobs and start execution. The next 20 jobs goes into Queue and then it executes remaining 5 jobs. So first 5 (1-5) and last 5 (26-30) jobs get executed and other (6-25) jobs goes into queue.

Expectation

The expected behaviour is that, first 5(1-5) should get executed as per corePoolSize and next 5 (6-10) should also get executed as per maxPoolSize and remaining 20 (11-30) should go into the queue.

Problem

I want to execute the job in a sequence but the problem is that last 5 (26-30) jobs executed first.

Is there any way I can change the behaviour as expected or any other Executor implementation avialable who meets the requirement?

Please guide.

1 Answers

The behaviour you see is due to the implementation of ThreadPoolExecutor when corePoolSize != maxPoolSize, which in JDK I have is below. Tasks 1-5 cause 5 addWorker calls, tasks 6-25 get queued. As the queue could be full by the time when submitting tasks 26-30 these end up in the else if and cause more addWorker calls. Thus tasks 26-30 may be run out of expected sequence if tasks 1-5 are running and the work queue is full with tasks 6-25. Adding more than 30 tasks would give rise to RejectedExecutionException.

if (workerCountOf(c) < corePoolSize) {
    if (addWorker(command, true))
        return;
    ...
}
if (isRunning(c) && workQueue.offer(command)) {
    int recheck = ctl.get();
    if (! isRunning(recheck) && remove(command))
        reject(command);
    else if (workerCountOf(recheck) == 0)
        addWorker(null, false);
}
else if (!addWorker(command, false))
    reject(command);

You could use an unbounded LinkedBlockingQueue which means that items 1-30 are added to the executor in expected order:

 ThreadPoolExecutor exeSer = new ThreadPoolExecutor(5, 10, 0, TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>());

Note that above might introduce other issues if adding large numbers of tasks and won't guarantee execution order 1 to 30 because at any stage up to maxPoolSize tasks may be run at once there is no guarantee which one of those task start first.

Related