Java concurrent programming-the blocking queue version of the producer consumer model

Producer consumer model

The producer consumer model is to solve the problem of strong coupling between producers and consumers through a container . The producer and the consumer do not communicate directly with each other, but communicate through the blocking queue , so the producer does not need to wait for the consumer to process the data after producing the data, and throws it directly to the blocking queue. The consumer does not ask the producer for the data, but Directly taken from the blocking queue, the blocking queue is equivalent to a buffer, which balances the processing capabilities of producers and consumers. This blocking queue is used to decouple producers and consumers .

Traditional edition

Realize with ReentrantLock.

package mytest;
/**
 * 共享资源类
 */
class ShareData {
    
    
    private int num = 0;
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    public void increment() throws Exception {
    
    
        lock.lock();
        try {
    
    
            //判断
            while (num != 0) {
    
    
                //等待 不生产
                condition.await();
            }
            //干活
            num++;
            System.out.println(Thread.currentThread().getName() + "\t" + num);
            //通知唤醒
            condition.signalAll();
        } finally {
    
    
            lock.unlock();
        }
    }

    public void deIncrement() throws Exception {
    
    
        lock.lock();
        try {
    
    
            //判断
            while (num == 0) {
    
    
                //等待 不生产
                condition.await();
            }
            //干活
            num--;
            System.out.println(Thread.currentThread().getName() + "\t" + num);
            //通知唤醒
            condition.signalAll();
        } finally {
    
    
            lock.unlock();
        }
    }
}
/**
 * Description
 * 一个初始值为0的变量 两个线程交替操作 一个加1 一个减1来5轮
 *
 * @author [email protected]
 * @version 1.0
 * @date 2019-04-13 14:01
 **/
public class ProdConsumerTraditionDemo {
    
    
    public static void main(String[] args) {
    
    
        ShareData shareData = new ShareData();
        new Thread(() -> {
    
    
            for (int i = 1; i <= 5; i++) {
    
    
                try {
    
    
                    shareData.increment();
                } catch (Exception e) {
    
    
                    e.printStackTrace();
                }
            }
        }, "AA").start();
        new Thread(() -> {
    
    
            for (int i = 1; i <= 5; i++) {
    
    
                try {
    
    
                    shareData.deIncrement();
                } catch (Exception e) {
    
    
                    e.printStackTrace();
                }
            }
        }, "BB").start();
    }
}
 

Blocking queue version

The blocking queue, as the name suggests, is first of all a queue, and the role of a blocking queue in the data structure is roughly as shown in the figure:
Insert picture description here
When the blocking queue is empty, the operation of getting elements from the queue will be blocked.
When blocked. When the queue is full, the operation of adding elements to the queue will be blocked.
Similarly
, threads that try to add new roundness to the full blocked queue will also be blocked, knowing that other threads remove one or more from the queue After the element or all empties the queue, the queue becomes free again and subsequently added.

Why use it? what is the benefit?

In the field of multithreading: the so-called blocking, in some cases the thread will be suspended (that is, thread blocking), once the condition is met, the suspended thread will be automatically awakened

Why you need to use BlockingQueue

The advantage is that we don't need to care about when we need to block the thread and when we need to wake up the thread, because BlockingQueue is all done for you.

Before the release of the concurrent package, in a multithreaded environment, each of us programmers must control these details by themselves, especially taking into account efficiency and thread safety, and this will bring a lot of complexity to our programs.

The core method of BlockingQueue

There are four ways to insert elements in BlockingQueue, four to remove, and two to check.
Insert picture description here
Actions of different types of methods.

Method type action
Throw an exception When the blocking queue is full, adding an element to the queue will throw IllegalStateException: Queue full
When the blocking queue is empty, noSuchElementException will be thrown back when removing elements from the queue
Special value Insert method, return true on success, return false on failure,
remove method, return element on success, return null if there is none in the queue
Keeps blocking When the blocking queue is full, the producer continues to put elements in the queue, and the queue will block until the put data or exit in response to the interrupt.
When the blocking queue is empty, the consumer tries to take elements from the queue, and the queue will block the consumer thread until the queue is available. .
Timeout exit When the blocking queue is full, the queue will block the producer thread for a certain period of time, and the producer thread will exit after the time limit is exceeded

Architecture introduction

Insert picture description here

Species analysis

ArrayBlockingQueue: A bounded blocking queue composed of an array structure.
LinkedBlockingDeque: A bounded (but the size default value Integer>MAX_VALUE) blocking queue composed of a linked list structure.
PriorityBlockingQueue: an unbounded blocking queue that supports priority sorting.
DelayQueue: Use a priority queue The implemented delay unbounded blocking queue.
SynchronousQueue: A blocking queue that does not store elements, that is, a single element queue.
LinkedTransferQueue: An unbounded blocking queue composed of a linked list structure.
LinkedBlockingDeque: A two-way blocking queue composed of an understanding structure.

Code

class MyResource {
    
    
    /**
     * 默认开启 进行生产消费的交互
     */
    private volatile boolean flag = true;
    /**
     * 默认值是0
     */
    private AtomicInteger atomicInteger = new AtomicInteger();

    private BlockingQueue<String> blockingQueue = null;

    public MyResource(BlockingQueue<String> blockingQueue) {
    
    
        this.blockingQueue = blockingQueue;
        System.out.println(blockingQueue.getClass().getName());
    }

    public void myProd() throws Exception {
    
    
        String data = null;
        boolean returnValue;
        while (flag) {
    
    
            data = atomicInteger.incrementAndGet() + "";
            returnValue = blockingQueue.offer(data, 2L, TimeUnit.SECONDS);
            if (returnValue) {
    
    
                System.out.println(Thread.currentThread().getName() + "\t 插入队列数据" + data + "成功");
            } else {
    
    
                System.out.println(Thread.currentThread().getName() + "\t 插入队列数据" + data + "失败");
            }
            TimeUnit.SECONDS.sleep(1);
        }
        System.out.println(Thread.currentThread().getName() + "\t 停止 表示 flag" + flag);
    }

    public void myConsumer() throws Exception {
    
    
        String result = null;
        while (flag) {
    
    
            result = blockingQueue.poll(2L, TimeUnit.SECONDS);
            if(null==result||"".equalsIgnoreCase(result)){
    
    
                flag=false;
                System.out.println(Thread.currentThread().getName()+"\t"+"超过2m没有取到 消费退出");
                System.out.println();
                System.out.println();
                return;
            }
            System.out.println(Thread.currentThread().getName() + "消费队列" + result + "成功");

        }
    }
    public void stop() throws Exception{
    
    
        flag=false;
    }
}

/**
 * Description
 * volatile/CAS/atomicInteger/BlockQueue/线程交互/原子引用
 *
 * @author [email protected]
 * @version 1.0
 * @date 2019-04-13 14:02
 **/
public class ProdConsumerBlockQueueDemo {
    
    
    public static void main(String[] args) throws Exception {
    
    
        MyResource myResource = new MyResource(new ArrayBlockingQueue<>(10));
        new Thread(()->{
    
    
            System.out.println(Thread.currentThread().getName()+"\t生产线程启动");
            try {
    
    
                myResource.myProd();
            } catch (Exception e) {
    
    
                e.printStackTrace();
            }
        },"Prod").start();

        new Thread(()->{
    
    
            System.out.println(Thread.currentThread().getName()+"\t消费线程启动");
            try {
    
    
                myResource.myConsumer();
            } catch (Exception e) {
    
    
                e.printStackTrace();
            }
        },"consumer").start();
        try {
    
     TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) {
    
     e.printStackTrace(); }
        System.out.println();
        System.out.println();
        System.out.println();
        System.out.println("时间到,停止活动");
        myResource.stop();
    }
}

Reference

Producer consumer model-detailed explanation and code implementation c++ version

Guess you like

Origin blog.csdn.net/e891377/article/details/108731232