Instead of filling a queue with 15,000,000 bulbs, let's fill 15 queues with 1,000,000 bulbs each:
private static final int MAX_PROD_BULBS = 15 _000_000;
private static final int CHUNK_BULBS = 1 _000_000;
private static final Random rnd = new Random();
private static final Queue<BlockingQueue<String>> chunks
= new LinkedBlockingQueue<>();
...
private static Queue<BlockingQueue<String>> simulatingProducers() {
logger.info("Simulating the job of the producers overnight ...");
logger.info(() -> "The producers checked "
+ MAX_PROD_BULBS + " bulbs ...");
int counter = 0;
while (counter < MAX_PROD_BULBS) {
BlockingQueue chunk = new LinkedBlockingQueue<>(CHUNK_BULBS);
for (int i = 0; i < CHUNK_BULBS; i++) {
chunk.offer("bulb-" + rnd.nextInt(1000));
}
chunks.offer(chunk);
counter += CHUNK_BULBS;
}
return chunks;
}
And, let's fire up 15 tasks using the following code:
while (!chunks.isEmpty()) {
Consumer consumer = new Consumer(chunks.poll());
consumerService.execute(consumer);
}
Each Consumer loops 1,000,000 bulbs using this code:
private static class Consumer implements Runnable {
private final BlockingQueue<String> bulbs;
public Consumer(BlockingQueue<String> bulbs) {
this.bulbs = bulbs;
}
@Override
public void run() {
while (!bulbs.isEmpty()) {
String bulb = bulbs.poll();
if (bulb != null) {}
}
}
}
The following graph represents the collected data for 10 runs:
This time, it looks like the work-stealing thread pool worked as a regular thread pool.