Java blocking queue BlockingQueue detailed explanation: producer consumer problem

BlockingQueue solution

Any effective solution for producers and consumers must control the generation of productive resources of put()calling methods as well as the consumer resource-consuming take()method call. Once control of method blocking is achieved, the problem can be solved.

Java provides out-of-the-box support to control the invocation of such methods, where one thread is creating resources and another thread is consuming resources BlockingQueue. The Java BlockingQueue interface java.util.concurrent in the package represents a queue that the thread can safely put in and get instances from.

1233356-9f2c986e3ae4826c.png

BlockingQueue is a construct where one thread putting resources into it, and another thread taking from it.

This is exactly what is needed to solve the problems of producers and consumers. Let us solve the problem now!

Use BlockingQueue to solve producer and consumer problems

Producer

The following code is for producer thread.

class Producer implements Runnable 
{
    protected BlockingQueue<Object> queue;
 
    Producer(BlockingQueue<Object> theQueue) {
        this.queue = theQueue;
    }
 
    public void run() 
    {
        try
        {
            while (true) 
            {
                Object justProduced = getResource();
                queue.put(justProduced);
                System.out.println("Produced resource - Queue size now = "  + queue.size());
            }
        } 
        catch (InterruptedException ex) 
        {
            System.out.println("Producer INTERRUPTED");
        }
    }
 
    Object getResource() 
    { 
        try
        {
            Thread.sleep(100); // simulate time passing during read
        } 
        catch (InterruptedException ex) 
        {
            System.out.println("Producer Read INTERRUPTED");
        }
        return new Object();
    }
}

Here, the producer thread creates resources (ie objects) and puts them in the queue. If the queue is full (maximum size is 20); then it will wait – until the consumer thread extracts resources from it. Therefore, the queue size never exceeds the maximum value, which is 20.

consumer

The following code applies to consumer threads.

class Consumer implements Runnable 
{
    protected BlockingQueue<Object> queue;
 
    Consumer(BlockingQueue<Object> theQueue) {
        this.queue = theQueue;
    }
 
    public void run() {
        try
        {
            while (true) 
            {
                Object obj = queue.take();
                System.out.println("Consumed resource - Queue size now = "  + queue.size());
                take(obj);
            }
        } 
        catch (InterruptedException ex) 
        {
            System.out.println("CONSUMER INTERRUPTED");
        }
    }
 
    void take(Object obj) 
    {
        try
        {
            Thread.sleep(100); // simulate time passing
        } 
        catch (InterruptedException ex) 
        {
            System.out.println("Consumer Read INTERRUPTED");
        }
        System.out.println("Consuming object " + obj);
    }
}

The consumer thread pulls the resource out of the queue (if it exists), otherwise it will wait and then check again when the producer puts something in it.

Test producer consumer solutions

Now let's test the producer and consumer components written above.

public class ProducerConsumerExample 
{
    public static void main(String[] args) throws InterruptedException 
    {
        int numProducers = 4;
        int numConsumers = 3;
         
        BlockingQueue<Object> myQueue = new LinkedBlockingQueue<>(20);
         
        for (int i = 0; i < numProducers; i++){
            new Thread(new Producer(myQueue)).start();
        }
             
        for (int i = 0; i < numConsumers; i++){
            new Thread(new Consumer(myQueue)).start();
        }
 
        // Let the simulation run for, say, 10 seconds
        Thread.sleep(10 * 1000);
 
        // End of simulation - shut down gracefully
        System.exit(0);
    }
}

When you run the code, you will find output similar to the following:

消耗的资源-队列大小现在= 1
产生的资源-队列大小现在= 1
消耗的资源-队列大小现在= 1
消耗的资源-队列大小现在= 1
产生的资源-队列大小现在= 1
产生的资源-队列大小现在= 1
产生的资源-队列大小现在= 1
消费对象java.lang.Object@14c7f728
消耗的资源-队列大小现在为0
消耗对象java.lang.Object@2b71e323
消耗的资源-队列大小现在为0
产生的资源-队列大小现在= 0
产生的资源-队列大小现在= 1
产生的资源-队列大小现在= 2
消耗对象java.lang.Object@206dc00b
消耗的资源-队列大小现在= 1
产生的资源-队列大小现在= 2
产生的资源-队列大小现在= 3
消耗对象java.lang.Object@1a000bc0
消耗的资源-队列大小现在= 2
消费对象java.lang.Object@25b6183d
消耗的资源-队列大小现在= 1
产生的资源-队列大小现在= 2
产生的资源-队列大小现在= 3
...
...
产生的资源-队列大小现在= 20
消耗对象java.lang.Object@2b3cd3a6
消耗的资源-队列大小现在= 19
产生的资源-队列大小现在= 20
消耗对象java.lang.Object@3876982d
消耗的资源-队列大小现在= 19
产生的资源-队列大小现在= 20

The output clearly shows that the queue size will never exceed 20, and the consumer thread is processing the queue resources placed by the producer thread. It's that simple.

References

https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/BlockingQueue.html
https://howtodoinjava.com/java/multi-threading/producer-consumer-problem-using-blockingqueue/


Kotlin developer community

1233356-4cc10b922a41aa80

The public account of the first Kotlin developer community in China, which mainly shares and exchanges related topics such as Kotlin programming language, Spring Boot, Android, React.js / Node.js, functional programming, and programming ideas.

The more noisy the world, the more peaceful thinking is needed.

1665 original articles published · 1067 praised · 750,000 views

Guess you like

Origin blog.csdn.net/universsky2015/article/details/105576771