Blocking queue (simulated implementation)

concept

        A blocking queue is a queue with a blocking function

characteristic

        When the queue is full, if you continue to enter the queue, you will be blocked until other threads take elements from the queue

        When the queue is empty, if you continue to leave the queue, it will also block until other threads add elements to the queue

features

        The blocking queue has the main characteristics of decoupling and peak-shaving and valley-filling

        1. Decoupling

                Two servers A, B, server A is responsible for sending user requests to server B, server B is responsible for solving user requests, but the two servers are not directly connected, but there is a blocking queue between the two servers, the server A sends the request to the blocking queue, and server B obtains the request from the blocking queue, so that there is no direct connection between the two servers, and the coupling is reduced.

                Reduced coupling prevents situations where an error on one server causes an error on another server

        2. Shaving peaks and filling valleys

                There are also two servers A and B. Server A is responsible for sending user requests to server B. There is a blocking queue between the two servers. When some special situation occurs, server A sends a large amount of data to the queue. Due to blocking If the queue is full and then add data, there will be blocking waiting, so the rhythm of server B processing data will not be affected, but if there is no blocking queue, if server A and server B are directly connected, it is very likely that server A will send a large number of requests to the server B will cause server B to crash

Use of blocking queues provided by the system

BlockingQueue<Integer> queue=new ArrayBlockingQueue(); //底层是顺序表
BlockingQueue<Integer> queue=new LinkedBlockingQueue();  //底层是链表
BlockingQueue<Integer> queue=new PriorityBlockingQueue();  //底层是优先级队列(堆)

        Calling the put method is to add data to the queue, and calling the take method is to pop the data in the queue. Since BlockingQueue implements the Queue interface, it also has methods such as offer and poll, but only the put and take methods are blocked, so it is recommended to use put and take methods

code display

class MyBlockingQueue{
    //用循环顺序表来作为阻塞队列的底层实现
    private String[]items=new String[1000];
    //数据存在的范围是[head,tail)
    //避免出现内存可见性和指令重排序的问题,要对变量加上volatile
    //指向头部的指针
    private volatile int head=0;
    //指向尾部的指针
    private volatile int tail=0;
    //记录阻塞队列中的数据个数
    private volatile int size=0;


    //put和take方法在多线程中涉及到多个线程改变同一个变量,所以要加上锁
    //向阻塞队列插入数据
    public void put(String elem) throws InterruptedException {
        synchronized(this){
            //判断队列是否满了
            while (size>=items.length){ //不一定阻塞等待是被notify正常唤醒的,也就不一定队列真的不满了,所以要用while循环多次判断
                                        //直到真的队列没满才进行接下来的操作(wait进行阻塞等待推荐和while一起使用,多次判断是否满足唤醒条件)
                this.wait();    //当队列满了就进入阻塞等待
            }
            items[tail]=elem;
            tail++;
            size++;
            if(tail>=items.length){
                tail=0;
            }
            this.notify();  //当向阻塞队列插入数据后,便可以唤醒弹出数据的阻塞状态
        }
    }

    //弹出阻塞队列中的数据
    public String take() throws InterruptedException {
        synchronized(this){
            //判断阻塞队列是否为空
            while (size==0){//不一定阻塞等待是被notify正常唤醒的,也就不一定队列真的不为空,所以要用while循环多次判断
                        //直到真的队列不为空才进行接下来的操作(wait进行阻塞等待推荐和while一起使用,多次判断是否满足唤醒条件)
                this.wait();
            }
            String elem=items[head];
            head++;
            size--;
            if(head>=items.length){
                head=0;
            }

            this.notify();  //弹出一个数据后队列不满了就可以唤醒插入数据的阻塞状态
            return elem;
        }
    }

}

        code analysis

                1. The underlying structure of the blocking queue in this code is a very simple circular array

                2. Since multi-threaded programming is required, and the put and take operations involve multiple threads modifying the same variable, it is necessary to use synchronized to lock the put and take methods, and in order to prevent memory visibility and instruction reordering problems , add the volatile keyword to the variable that needs to be read and modified in multithreading to prevent the compiler from optimizing.

Regarding synchronized and memory visibility and instruction reordering, you can see Thread Safety Issues , Thread Safety Issues (Memory Visibility) , Thread Safety Issues (Instruction Reordering)

                3. How to achieve blocking? In the put method, if the queue is full, it will enter the blocking wait, and use the synchronized locked object to call the wait method to enter the blocking wait. Only when the take method is executed successfully, can call notify to wake up the blocked waiting thread in the put method.

                Also in the take method, if the queue is empty, it will enter the blocking wait, and use the synchronized locked object to call the wait method to enter the blocking wait. Only when the put method is executed successfully, can call notify to wake up the blocked waiting thread in the take method . Regarding wait and notify, you can see that thread execution order is coordinated through wait and notify

                4. The put and take methods need to lock the same object, because the variables to be modified in put and take are the same, so there are thread safety problems when multiple threads call the put and take methods, so the same object must be locked Lock it.

                5. The condition for judging whether to enter the blocking wait is a while loop (recommended to use wait() together with while), because it is not necessarily blocked waiting wait is normally awakened by notify, so it is not necessarily true whether the queue is full or not empty Conditions, so multiple judgments are required, if the queue is still full or empty, it will enter the blocking wait again

Guess you like

Origin blog.csdn.net/q322359/article/details/131998041