I have been reading about the thread-pool pattern and I can\'t seem to find the usual solution for the following problem.
I sometimes want tasks to be executed serial
Use two Active Objects. In two words: active object pattern consists from priority queue and 1 or many working threads those can get tasks from queue and process its.
So use one active object with one working thread: all tasks those would be places to queue would be processed sequentially. Use second active object with number of working thread more then 1. In this case working threads would get and process tasks from queue in any order.
Luck.
To do what you want to do with a threadpool, you might have to create some kind of scheduler.
Something like that:
TaskQueue -> Scheduler -> Queue -> ThreadPool
Scheduler runs in its own thread, keeping tracks of dependencies between jobs. When a job is ready to be done, the scheduler just pushes it in the queue for the threadpool.
The ThreadPool might have to send signals to the Scheduler to indicate when a job is done so the scheduler can put jobs depending on that job into the Queue.
In your case, the dependencies could probably be stored in a linked list.
Let's say you have the following dependencies: 3 -> 4 -> 6 -> 8
Job 3 is running on the threadpool, you still have no ideas that job 8 exists.
Job 3 ends. You remove the 3 from the linked list, you put job 4 on the queue to the threadpool.
Job 8 arrives. You put it at the end of the linked list.
The only constructs that have to be fully synchronized are the Queues before and after the scheduler.
How would you ensure those tasks are ordered?
push task1
push task2
push task346
push task5
In response to the edit:
push task1
push task27 **
push task3468 *
push task5
push task9
Thread Pool with ordered and unordered execute methods:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class OrderedExecutor {
private ExecutorService multiThreadExecutor;
// for single Thread Executor
private ThreadLocal<ExecutorService> threadLocal = new ThreadLocal<>();
public OrderedExecutor(int nThreads) {
this.multiThreadExecutor = Executors.newFixedThreadPool(nThreads);
}
public void executeUnordered(Runnable task) {
multiThreadExecutor.submit(task);
}
public void executeOrdered(Runnable task) {
multiThreadExecutor.submit(() -> {
ExecutorService singleThreadExecutor = threadLocal.get();
if (singleThreadExecutor == null) {
singleThreadExecutor = Executors.newSingleThreadExecutor();
threadLocal.set(singleThreadExecutor);
}
singleThreadExecutor.submit(task);
});
}
public void clearThreadLocal() {
threadLocal.remove();
}
}
After filling all queues the threadLocal should be cleared. The only drawback is that singleThreadExecutor will be created each time the method
executeOrdered(Runnable task)
invoked in separate thread
This is achievable, well, as far as I understand your scenario. Basically what you need is do something smart to coordinate your tasks in main thread. Java API your need are ExecutorCompletionService and Callable
First, implement your callable task:
public interface MyAsyncTask extends Callable<MyAsyncTask> {
// tells if I am a normal or dependent task
private boolean isDependent;
public MyAsyncTask call() {
// do your job here.
return this;
}
}
Then in your main thread, use CompletionService coordinate the dependent task execution (i.e. a wait mechanism):
ExecutorCompletionService<MyAsyncTask> completionExecutor = new
ExecutorCompletionService<MyAsyncTask>(Executors.newFixedThreadPool(5));
Future<MyAsyncTask> dependentFutureTask = null;
for (MyAsyncTask task : tasks) {
if (task.isNormal()) {
// if it is a normal task, submit it immediately.
completionExecutor.submit(task);
} else {
if (dependentFutureTask == null) {
// submit the first dependent task, get a reference
// of this dependent task for later use.
dependentFutureTask = completionExecutor.submit(task);
} else {
// wait for last one completed, before submit a new one.
dependentFutureTask.get();
dependentFutureTask = completionExecutor.submit(task);
}
}
}
By doing this, you use a single executor (threadpool size 5) execute both normal and dependent tasks, the normal task are executed immediately as soon as submitted, the dependent tasks are executed one by one (wait are performed in main thread by calling get() on Future before submitting new dependent task), so at any point of time, you always have a number of normal tasks and a single dependent task (if exists) running in a single threadpool.
This is just a head start, by using ExecutorCompletionService, FutureTask and Semaphore, you can implement more complex thread coordination scenario.
Basically, there are a number of pending tasks. Some of the tasks can only be performed when one or more other pending tasks have finished executing.
The pending tasks can be modeled in a dependency graph:
So there is (at least) one thread used to add/remove pending tasks, and there is a thread pool of working threads.
When a task is added to the dependency graph, you must check:
Performance:
Assumptions:
As you may have read between the lines, you must design the tasks so that they don't interfere with other tasks. Also, there must be a way to determine the priority of the tasks. The task priority should include the data handled by each task. Two tasks may not alter the same object simultaneously; one of the tasks should get priority over the other one instead, or the performed operations on the object must be thread-safe.