docs: Adding explanation for Producer-Consumer Design Pattern (#2179)

This commit is contained in:
Manvi Goel
2022-11-05 14:09:54 +05:30
committed by GitHub
parent 6f21b10607
commit 3605f8fefb
+108
View File
@@ -11,6 +11,114 @@ Producer Consumer Design pattern is a classic concurrency pattern which reduces
coupling between Producer and Consumer by separating Identification of work with Execution of
Work.
## Explanation
Real-world example
> Consider a manufacturing process of item, the producer will need to pause the production when
> manufacturing pipeline is full and the consumer will need to pause the consumption of item
> when the manufacturing pipeline is empty. We can separate the process of production and consumption
> which work together and pause at separate times.
In plain words
> It provides a way to share data between multiple loops running at different rates.
Wikipedia says
> Dijkstra wrote about the case: "We consider two processes, which are called the 'producer'
> and the 'consumer' respectively. The producer is a cyclic process that produces a certain
> portion of information, that has to be processed by the consumer. The consumer is also a cyclic
> process that needs to process the next portion of information, as has been produced by the producer
> We assume the two processes to be connected for this purpose via a buffer with unbounded capacity."
**Programmatic Example**
Take our Producer and Consumer example from above. Consider we have a `Item` class that is produced by `Producer` class and is added to the `ItemQueue`.
```java
public class Producer {
private static final SecureRandom RANDOM = new SecureRandom();
private final ItemQueue queue;
private final String name;
private int itemId;
public Producer(String name, ItemQueue queue) {
this.name = name;
this.queue = queue;
}
/**
* Put item in the queue.
*/
public void produce() throws InterruptedException {
var item = new Item(name, itemId++);
queue.put(item);
Thread.sleep(RANDOM.nextInt(2000));
}
}
```
Then, we have the `Consumer` class that takes the item from the item queue.
```java
@Slf4j
public class Consumer {
private final ItemQueue queue;
private final String name;
public Consumer(String name, ItemQueue queue) {
this.name = name;
this.queue = queue;
}
/**
* Consume item from the queue.
*/
public void consume() throws InterruptedException {
var item = queue.take();
LOGGER.info("Consumer [{}] consume item [{}] produced by [{}]", name,
item.getId(), item.getProducer());
}
}
```
Now, during the manufacturing pipeline, we can instantiate objects from both the `Producer` and `Consumer` clasess as they produce and consumer items from the queue.
```java
var queue = new ItemQueue();
var executorService = Executors.newFixedThreadPool(5);
for (var i = 0; i < 2; i++) {
final var producer = new Producer("Producer_" + i, queue);
executorService.submit(() -> {
while (true) {
producer.produce();
}
});
}
for (var i = 0; i < 3; i++) {
final var consumer = new Consumer("Consumer_" + i, queue);
executorService.submit(() -> {
while (true) {
consumer.consume();
}
});
}
```
## Class diagram
![alt text](./etc/producer-consumer.png "Producer Consumer")