多线程之阻塞队列

什么是阻塞队列:

阻塞队列是一种特殊的队列数据结构,具有阻塞的特性。它主要用于多线程编程中,提供了线程安全的数据交换的方法。

  1. 当队列满的时候,入队列就会阻塞,除非另一方拿走队列里边的数据。
  2. 当队列空的时候,如果这时候有人拿走数据也会阻塞,因为已经没有数据了,除非有人往队列中加数据。

阻塞队列常常用来实现生产者消费者模型,也是面试中常考的部分:

什么是生产者消费者模型:

生产者消费者模型是一种经典的多线程或多进程设计模式。在这个模型中,主要有两类角色:生产者和消费者。在这个模型中,生产者将数据放入阻塞队列中,而消费者则从中拿数据,实际上也就是生产者和消费者通过阻塞队列进行通信。


生产者消费者模型在开发中的常见使用场景:

1.阻塞队列相当于一个缓冲区域,平衡了消费者和生产者之间的关系:

比如某游戏在“周年庆”等活动场景下,游戏的服务器同一时刻可能会收到大量的登录请求。如果直接处理这些请求,服务器可能扛不住。这个时候就可以把这些请求都放到一个阻塞队列中,然后再由消费者线程慢慢处理每个登录请求。

2.生产者和消费者之间能够解耦

生产者和消费者之间,生产者不用关系谁拿走了数据,而消费者不用关心谁给他的数据,这就是解耦,通过这样的模型使得他们之间没有重合的部分能够各司其职。

java中的阻塞队列:

BlockingQueue是一个接口,我们要向下转型从而实现阻塞队列。

BlockingQueue有两个主要方法put(用于放入元素),take(用于拿取元素)

我们要开始实现简单的BlockIngQueue了

这里的1000是该阻塞队列的容量。

这就是阻塞队列,在示例中,创建了一个容量为 1000 的 ArrayBlockingQueue 对象 bq,作为生产者和消费者之间的共享缓冲区。然后创建了两个线程thread和thread1,分别表示生产者和消费者。

由于在消费者中,take 一次就睡眠0.5秒,使上述代码只take一次后,bq 队列就直接 put 满了,后续则会生产一个消费一个。

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class Demo18 {
    public static void main(String[] args) {
        BlockingQueue<Integer> blockingQueue=new ArrayBlockingQueue<>(1000);

        Thread thread=new Thread(()->{
            int count=0;
            while (true){
                try {
                    blockingQueue.put(count);
                    System.out.println("阻塞队列中存储了"+count);
                    
                    count++;
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        Thread thread1=new Thread(()->{
            while (true){
                try {
                    int count=blockingQueue.take();
                    System.out.println("阻塞队列中消费了"+count);
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        thread.start();
        thread1.start();

    }
}


自己实现一个阻塞队列:

  1. 这是一个循环队列
  2. 通过synchronized来防止出现线程安全的问题
  3. 这里的wait是互相唤醒,必须牢记。
public class MyBlockingQueue {
    private Integer[] elem;
    private int head;
    private int tail;
    private int size;//阻塞队列中的个数

    public MyBlockingQueue(int size) {
        elem=new Integer[size];
    }


    //放入方法
    public void put(Integer input) throws InterruptedException {
        //使用while而不是if是为了防止if被notify打断
        synchronized (this) {
            while (size >= elem.length) {
                this.wait();
            }
            elem[tail] = input;
            tail++;
            if (tail >= elem.length) {
                tail = 0;

            }
            //到最后已经放入完成后
            size++;
            //唤醒下边的wait
            this.notify();
        }

    }

    public Integer take() throws InterruptedException {
        synchronized (this) {
            while (size == 0) {
                //这里被上面的notify唤醒
                this.wait();
            }

            Integer take = elem[head];
            head++;
            if (head>=elem.length){
                head=0;
            }
            size--;
            //唤醒等待元素
            this.notify();
            return take;


        }
    }


}
public class Demo19 {
    public static void main(String[] args) throws InterruptedException {
        MyBlockingQueue myBlockingQueue=new MyBlockingQueue(100);
        Thread thread=new Thread(()->{
            int count=0;
           while (true){
               try {
                   myBlockingQueue.put(count);
                   System.out.println("生产了"+count);
                   count++;

               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
           }
        });
        Thread thread1=new Thread(()->{
            while (true){
                try {
                    Integer take = myBlockingQueue.take();
                    System.out.println("消费了"+take);
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        thread.start();
        Thread.sleep(100);
        thread1.start();
    }
}

总结:

  1. 阻塞队列中给我们实现了加锁,也就是没有线程安全问题。
  2. 生产者消费者模型是阻塞队列的具体实现
  3. Java 提供了多种阻塞队列的实现,包括 ArrayBlockingQueueLinkedBlockingQueuePriorityBlockingQueue 等。
  4. 一些阻塞队列具有固定的容量限制,如 ArrayBlockingQueue,而另一些则可以是无界的,如 LinkedBlockingQueue

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值