Java中的阻塞队列

队列就是我们常说的先进先出的线性数据结构。

而阻塞队列是继承自队列的,相对于队列来说,多了两个重要的方法

  • put方法 : put方法的作用是插入元素,通常在队列没有满的时候是正常插入。如果队列满了无法继续插入,这时它不会立刻返回false和抛出异常,而是让插入的线程进入阻塞状态等待,直到队列里面有空闲空间了。此时队列就会让之前的线程解除阻塞状态,并把刚才那个元素添加进去
  • take方法 : take方法的作用是获取并移除队列的头节点。通常队列里面有元素会正常取出数据并移除;但是如果执行take的时候队列里无数据,则阻塞等待,直到队列里面有数据以后,就会解除阻塞状态,去获取数据。

Java中是有阻塞队列的实现的.

  • BlockingQueue 是阻塞队列的接口

对于接口也有多种类型不同的实现

  • LinkedBlockingQueue
  • ArrayBlockingQueue

使用多线程基础 和 数组知识实现一个简单的阻塞队列.(完成读写操作)

package hello;

class MyBlockingQueue{
    //保存数据本体
    private int[] data = new int[1000];
    //有效元素个数
    private int size = 0;
    //队首下标
    private int head = 0;
    //队尾下标
    private int tail = 0;

    //入队列
    synchronized public void put(int value) throws InterruptedException {
        if(size == data.length){
//            System.out.println("队满");
//            return;
            this.wait();
        }
        //把新的元素放到tail的位置上
        data[tail] = value;
        tail++;
//        tail %= data.length;
        if(tail >= data.length){
            tail = 0;
        }
        size++;
        //有人等待,notify就会唤醒.没人等待,不会有任何副作用
        this.notify();
    }

    //出队列
    synchronized public Integer take() throws InterruptedException {
        //判空
        if(size == 0){
//            return null;
            this.wait();
        }
        //取出head位置的元素
        int ret = data[head];
        head++;
        if(head >= data.length){
            head = 0;
        }
        size--;
        this.notify();
        return ret;
    }

}

public class Test {

private static MyBlockingQueue queue = new MyBlockingQueue();

    public static void main(String[] args) {

        Thread producer = new Thread(() -> {
            int num = 0;
            while(true){
                try{
                    System.out.println("生产了: "+ num);
                    queue.put(num);
                    num++;
                }catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        producer.start();

        Thread customer = new Thread(()->{
            while (true) {
                try {
                    int num = queue.take();
                    System.out.println("消费了: "+num);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        customer.start();
    }
    //生产者消费者模型测试
}

小bug

  1. 在实际运行中,我们的代码在判空和判满的时候有可能会被打断。
  • 比如:很可能在别的代码里暗中 interrupt(),把wait()给提前唤醒了。 明明条件还没有满足(队列非空),但是wait唤醒之后就继续往下走了。所以更稳妥的做法是,我们在wait之后再进行判断一次。(使用while多次判断)
  1. 防止出现内存可见性优化。给计数变量添加 volatile 关键字
class MyBlockingQueue{
    //保存数据本体
    private int[] data = new int[1000];
    //有效元素个数
    volatile private int size = 0;
    //队首下标
    volatile private int head = 0;
    //队尾下标
    volatile private int tail = 0;

    //入队列
    synchronized public void put(int value) throws InterruptedException {
        //判满  将 if 改为 while
        while (size == data.length){
            this.wait();
        }
        //把新的元素放到tail的位置上
        data[tail] = value;
        tail++;
        if(tail >= data.length){
            tail = 0;
        }
        size++;
        //有人等待,notify就会唤醒.没人等待,不会有任何副作用
        this.notify();
    }

    //出队列
    synchronized public Integer take() throws InterruptedException {
        //判空  将 if 改为 while
        while (size == 0){
            this.wait();
        }
        //取出head位置的元素
        int ret = data[head];
        head++;
        if(head >= data.length){
            head = 0;
        }
        size--;
        this.notify();
        return ret;
    }

}

public class Test {

    private static MyBlockingQueue queue = new MyBlockingQueue();

    public static void main(String[] args) {

        Thread producer = new Thread(() -> {
            int num = 0;
            while(true){
                try{
                    System.out.println("生产了: "+ num);
                    queue.put(num);
                    num++;
                }catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        producer.start();

        Thread customer = new Thread(()->{
            while (true) {
                try {
                    int num = queue.take();
                    System.out.println("消费了: "+num);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        customer.start();
    }
    //生产者消费者模型测试
}