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

This commit is contained in:
Manvi Goel authored and GitHub committed 2022-11-05 10:39:54 +02:00
1 parent 6f21b10607
commit 3605f8fefb
1 file changed
+108
+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 coupling between Producer and Consumer by separating Identification of work with Execution of
Work. 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 ## Class diagram
![alt text](./etc/producer-consumer.png "Producer Consumer") ![alt text](./etc/producer-consumer.png "Producer Consumer")