Several ways to implement the producer and consumer model
Take the example in our lives as an example. Products produced by factories are always exported to use outside. This is the concept of production and consumption.
In our actual software development process, we often encounter the following scenarios: a module is responsible for generating data, and this data is processed by another module (the module here is generalized, which can be classes, functions, threads, processes, etc.).
The module that generates data is vividly called a producer; while the module that processes data is called a consumer.
The first type: Use wait-notify to implement the producer and consumer model
1. A producer and a consumer:
2. One producer and multiple consumers:
The second type: Using blocking queues to implement the producer and consumer model
3. Use blocking queues to implement producer-consumer pattern
I believe everyone has gone to Japanese cuisine. There is a very attractive meal that is barbecue. The barbecue master will stand on the side and keep barbecuing, and then put the roasted meat on a plate; and we diners who are drooling will sit on the side, and we will keep eating as long as there is meat on the plate.
In this life case, the barbecue master is the producer, and he is responsible for barbecue. After the roasting, he puts the meat on the plate instead of handing it directly to the diner (that is, there is no need to notify the diner to eat meat). If the meat on the plate is full, the master will stop for a while until someone goes to eat the barbecue before producing the meat; and the diners are just staring at the plate, and once there is meat on the plate, we are responsible for eating it;
During the whole process, diners and barbecue master did not deal directly with each other, but interacted with the plate.
The plate acts as a buffer concept. If something is produced, put it in. The plate also has a size limit. If it exceeds the size of the plate, it will block the producer's production and wait for the consumer to consume; when the plate is empty, it will block consumer consumption and wait for the producer to produce.
Blocking the queue during programming can realize the function of the disk.
Characteristics of blocking queues:
When the queue element is full, block the insertion operation;
When the queue element is empty, the acquisition operation is blocked.
Both ArrayBlockingQueue and LinkedBlockingQueue support FIFO (first in, first out), but LinkedBlockingQueue is unbounded, while ArrayBlockingQueue is bounded.
The following uses blocking queues to implement producers and consumers:
Producer:
import java.util.concurrent.BlockingQueue;public class Producer implements Runnable{private final BlockingQueue blockingQueue;//Set the size of the queue cache. The production will be temporarily stopped after exceeding this size during the production process. Private final int QUEUE_SIZE = 10;public Producer(BlockingQueue blockingQueue){this.blockingQueue = blockingQueue;}int task = 1;@Override public void run() {while(true){try {System.out.println("Production:" + task);//Put the produced product in the queue cache blockingQueue.put(task);++task;//Let it stop for a while to facilitate viewing the effect Thread.sleep(1000);}catch (InterruptedException e) {e.printStackTrace();}}}}consumer:
import java.util.concurrent.BlockingQueue;//Consumer public class Consumer implements Runnable{private final BlockingQueue blockingQueue;public Consumer(BlockingQueue blockingQueue){this.blockingQueue = blockingQueue;}@Override public void run() {//As long as there is a task in the blocking queue, it will continue to consume while(true){try {System.out.println("Consuming: " + blockingQueue.take());//Let it stop for a while to facilitate viewing the effect Thread.sleep(2000);}catch (InterruptedException e) {e.printStackTrace();}}}}test:
import java.util.concurrent.BlockingQueue;import java.util.concurrent.LinkedBlockingQueue;/** * Producer consumer mode* Use blocking queue BlockingQueue * @author wanggenshen * */public class TestConPro {public static void main(String[] args){BlockingQueue blockingQueue = new LinkedBlockingQueue(5);Producer p = new Producer(blockingQueue);Consumer c = new Consumer(blockingQueue);Thread tp = new Thread(p);Thread tc= new Thread(c);tp.start();tc.start();}}Because LinkedBlockingQueue is an unbounded queue, producers will continue to produce, put the produced tasks in the queue, and consumers will consume in the queue:
If you use the bounded blocking queue ArrayBlockingQueue instead, you can initialize the queue size. Then when the elements in the queue exceed the queue size, the producer will wait for the consumer to consume one and then produce the other:
Test code:
Initialize an ArrayBlockingQueue of size 10:
public static void main(String[] args){BlockingQueue blockingQueue = new ArrayBlockingQueue(10);Producer p = new Producer(blockingQueue);Consumer c = new Consumer(blockingQueue);Thread tp = new Thread(p);Thread tc= new Thread(c);tp.start();tc.start();}During the test, the producers were allowed to produce slightly faster, while the consumers were slower. You can see that the difference between the product serial number produced and the product serial number consumed is always 10 (the size of the queue):
Summarize
The above is the entire content of this article about the implementation method of production consumer model and the code examples of thread safety issues. I hope it will be helpful to everyone. Interested friends can continue to refer to other related topics on this site. If there are any shortcomings, please leave a message to point it out. Thank you friends for your support for this site!