Design patterns in Java: producer consumer pattern and observer pattern

1, Foreword

  in the previous article Design patterns in Java (1): observer pattern In today's article, we want to do a simple extended learning - compare the similarities and differences between the producer consumer model and the * observer model.

2, What is the "producer consumer model"?

  unlike the observer model, the producer consumer model itself does not belong to any of the design patterns. So what is the producer consumer model? Let's briefly illustrate it with an example:

  as shown in the above figure, producers and consumers are like contributors and subscribers to a magazine. There can be multiple contributors to the same magazine and multiple readers, A magazine is a bridge (i.e. buffer) connecting authors and readers. Through the data buffer of a magazine, authors can deliver completed works to readers who subscribe to the magazine. In this process, authors do not care whether readers receive works or complete reading. Authors and readers are two relatively independent objects, and their behaviors do not affect each other.

  as you can see, there are three roles in this example: producer, consumer and buffer. Producers and consumers have a better understanding. The former is production data, while the latter deals with the data produced by the former. The buffer plays a role of decoupling, supporting asynchrony and uneven busy and idle in the producer consumer model.

3, The difference between the two

1. Different programming paradigms

  the first difference between producer consumer mode and observer mode has been mentioned above. The former is a process oriented software design mode and does not belong to any of the 23 design modes proposed by Gang of Four, while the latter is one of the 23 design modes, that is, one of the object-oriented design modes.

2. Different relationships

  the difference in this concept leads to the next difference, that is, in the observer model, there is only one to many relationship, not many to many relationship, while in the producer consumer model, there is many to many relationship.

  in the observer mode, there is only one observer, but there can be multiple observers. For example, traffic lights at intersections, straight vehicles only observe and control straight traffic lights, and do not observe and control left or right turning traffic lights, that is, the object of observation is fixed and unique.

  the producer consumer model is different. There can be multiple producers and multiple consumers. Let's use the above example of authors and readers. In this example, readers only care about the content of the magazine and don't need to care about the creator of the content. The author only needs to know that the created works can be published to the corresponding magazine, and don't need to care about the readers.

3. Different coupling relationships

  from the previous difference, it is not difficult to see that the coupling relationship between producer consumer model and observer model is also different. The former is light coupling and the latter is heavy coupling.

4. Different application scenarios

  observer mode is mostly used in event driven models, while producer consumer mode mostly appears in inter process communication for decoupling and concurrent processing. Producer consumer mode is commonly used in message queue. Of course, using the producer consumer model in Java also needs to pay attention to the thread safety of the buffer, which will not be described here.

4, A small example

  finally, end this extended learning with a simple demo.

1. StoreQueue - buffer

public class StoreQueue<T> {  
private final BlockingQueue<T> queue = new LinkedBlockingQueue<>();  
/**  
* Add data to queue  
*  
* @param data Producer production data  
*/  
public void add(T data) {  
try { 
queue.put(data);  
} catch (Exception e) {
e.printStackTrace();  
}  
}  
/** 
* Get data from queue  
*  
* @return Data obtained from the queue  
*/ 
public T get() {  
try {  
return queue.take();  
} catch (Exception e) { 
e.printStackTrace();  
}  
return null;  
} 
}

  in this example, we use the blocking queue of jdk to implement a buffer. Here, we only need to implement the methods of putting data and getting data. If we implement a blocking queue ourselves, on the one hand, we need to pay attention to blocking processing, on the other hand, we need to consider thread safety. We won't expand the description here. Interested students can see the source code of BlockingQueue.

2. Producer - producer

public class Producer implements Runnable{  
private StoreQueue<String> storeQueue;  
public Producer(StoreQueue<String> storeQueue) {  
this.storeQueue = storeQueue;  
} 
@Override  
public void run() {  
for (int i = 0; i < 10; i++) {  
storeQueue.add(Thread.currentThread().getName() + ":" + i);  
}  
} 
}

3. Consumer - Consumer

public class Consumer implements Runnable{  
private StoreQueue<String> storeQueue;  
public Consumer(StoreQueue<String> storeQueue) {  
this.storeQueue = storeQueue; 
}  
@Override 
public void run() {  
try {  
while (true) {  
String data = storeQueue.get(); 
System.out.println("Current consumption thread : " + Thread.currentThread().getName() + ", Data received : " + data);  
}  
} catch (Exception e) {  
e.printStackTrace();  
Thread.currentThread().interrupt();  
}  
} 
}

4. Execution logic and operation results

Execution logic

public static void main(String[] args) {  
StoreQueue<String> storeQueue = new StoreQueue<>();  
Producer producer = new Producer(storeQueue);  
Consumer consumer = new Consumer(storeQueue);  
Producer producerTwo = new Producer(storeQueue);  
Consumer consumerTwo = new Consumer(storeQueue);  
new Thread(producer).start();  
new Thread(consumer).start();  
new Thread(producerTwo).start();  
new Thread(consumerTwo).start();  
}

Operation results

Current consumption thread : Thread-1, Data received : Thread-0:0 
Current consumption thread : Thread-1, Data received : Thread-0:1 
Current consumption thread : Thread-1, Data received : Thread-0:2 
Current consumption thread : Thread-1, Data received : Thread-0:3 
Current consumption thread : Thread-1, Data received : Thread-0:4 
Current consumption thread : Thread-3, Data received : Thread-0:5 
Current consumption thread : Thread-3, Data received : Thread-0:7 
Current consumption thread : Thread-3, Data received : Thread-0:8 
Current consumption thread : Thread-3, Data received : Thread-0:9 
Current consumption thread : Thread-3, Data received : Thread-2:0 When
 Pre consumer thread : Thread-3, Data received : Thread-2:1 
Current consumption thread : Thread-3, Data received : Thread-2:2 
Current consumption thread : Thread-3, Data received : Thread-2:3 
Current consumption thread : Thread-3, Data received : Thread-2:4 
Current consumption thread : Thread-3, Data received : Thread-2:5 
Current consumption thread : Thread-3, Data received : Thread-2:6 
Current consumption thread : Thread-3, Data received : Thread-2:7 
Current consumption thread : Thread-3, Data received : Thread-2:8 
Current consumption thread : Thread-3, Data received : Thread-2:9 
Current consumption thread : Thread-1, Data received : Thread-0:6

  it can be seen from the above data results that the data produced by different producers will only be consumed by one consumer, and there is no thread safety problem, which is due to the BlockingQueue used to implement the buffer.

This article is transferred from: developer.aliyun.com/article/78694...

Keywords: Java

Added by suckablesausage on Sun, 19 Dec 2021 18:56:37 +0200