My multi-threaded class is supposed to carry out three operations – operation1
, operation2
, and operation3
– on a number of objects of the class ClassA
, where each type of operation is dependant on the earlier operation. For this, I have tried to implement the producer-consumer pattern using a number of BlockingQueue
s and an ExecutorService
.
final ExecutorService executor = ForkJoinPool.commonPool();
final BlockingQueue<ClassA> operationOneQueue = new ArrayBlockingQueue<>(NO_OF_CLASS_A_OBJECTS);
final BlockingQueue<ClassA> operationTwoQueue = new ArrayBlockingQueue<>(NO_OF_CLASS_A_OBJECTS);
final BlockingQueue<ClassA> operationThreeQueue = new ArrayBlockingQueue<>(NO_OF_CLASS_A_OBJECTS);
final BlockingQueue<ClassA> resultQueue = new ArrayBlockingQueue<>(NO_OF_CLASS_A_OBJECTS);
The operations are implemented like this:
void doOperationOne() throws InterruptedException {
ClassA objectA = operationOneQueue.take();
objectA.operationOne();
operationTwoQueue.put(objectA);
}
where each type of operation has its own corresponding method, with its "own" in-queue and out-queue. Each operation method calls the appropriate method on the ClassA
object. The method doOperationThree
puts ClassA
objects in the resultQueue
, meaning they have been completely processed.
First, I fill the operationOneQueue
with all ClassA
objects that are to be operated on. Then, I try to assign executable tasks to the ExecutorService
like this:
while (resultQueue.size() < NO_OF_CLASS_A_OBJECTS) {
executor.execute(() -> {
try {
doOperationOne();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
executor.execute(() -> {
try {
doOperationTwo();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
executor.execute(() -> {
try {
doOperationThree();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
executor.shutdown();
Running my program, I get a java.util.concurrent.RejectedExecutionException
.
Operation1: ClassA object 0
Operation2: ClassA object 0
Operation1: ClassA object 1
Operation3: ClassA object 0
....
Operation1: ClassA object 46
Operation2: ClassA object 45
Operation3: ClassA object 45
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Queue capacity exceeded
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.growArray(ForkJoinPool.java:912)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.lockedPush(ForkJoinPool.java:867)
at java.base/java.util.concurrent.ForkJoinPool.externalPush(ForkJoinPool.java:1911)
at java.base/java.util.concurrent.ForkJoinPool.externalSubmit(ForkJoinPool.java:1930)
at java.base/java.util.concurrent.ForkJoinPool.execute(ForkJoinPool.java:2462)
at concurrent.operations.Program1.main(Program1.java:96)
What am I doing wrong? How can I achieve this without over-saturating the thread pool?
Edit: Full disclosure – this is homework with some requirements. 1. I must use ForkJoinPool.commonPool()
and must not set the number of threads myself, 2. I must use the consumer-producer pattern, and 3. I must not modify ClassA
.