Java并发编程之阻塞队列

本文围绕Java并发编程中的阻塞队列展开。介绍了阻塞队列的概念,即队列为空或满时操作会被阻塞。阐述了使用阻塞队列的好处,无需程序员手动控制线程阻塞与唤醒。还讲解了BlockingQueue的核心方法及SynchronousQueue的用法,最后说明了其应用场景,如生产者消费者模式、线程池等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java并发编程之阻塞队列


目录

  1. 阻塞队列概述
  2. 为什么用?有什么好处?
  3. BlockingQueue的核心方法
  4. SynchronousQueue的用法
  5. 用在哪里

1. 阻塞队列概述


  1. 阻塞队列,顾名思义,首先它是一个队列,而一个阻塞队列在数据结构中起到的作业大致如下图所示:
    在这里插入图片描述
  2. 当线程队列是空时,从队列中获取元素的操作将会被阻塞。
  3. 试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其他的线程往空的队列插入新的元素。
  4. 同样,试图往已满的阻塞队列中添加新元素的线程也同样会被阻塞,直到其他的线程从列总移除一个或多个元素或者完全清空队列后使队列重新变得空闲起来并后续新增。

2. 为什么用?有什么好处?


  1. 在多线程领域;所谓阻塞,在某些情况下会挂起线程(即阻塞),一旦条件满足,被挂起的线程又会自动被唤醒
  2. 为什么需要BlockingQueue?
    • 好处是我们不需要关心什么时候需要阻塞线程,什么时候需要唤醒线程,因为这一切BlockingQueue都给你一手包办了
  3. 在concurrent包发布以前,在多线程环境下,我们每个程序员都必须去自己控制这些细节,尤其还要兼顾效率和线程安全,而
    这会给我们的程序带来不小的复杂度。

3. BlockingQueue的核心方法


1. 核心方法概述

在这里插入图片描述
在这里插入图片描述


2. 核心方法api使用

1. 阻塞队列api之抛出异常组

代码:

public class BlockingQueueDemo {
    public static void main(String[] args) {
       BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println("添加元素" + blockingQueue.add("a"));
        System.out.println("添加元素" + blockingQueue.add("b"));
        System.out.println("添加元素" + blockingQueue.add("c"));
//        System.out.println("添加元素" + blockingQueue.add("d"));

        //检查队列第一个元素
        System.out.println(blockingQueue.element());

        System.out.println("移除元素" + blockingQueue.remove());
        System.out.println("移除元素" + blockingQueue.remove());
        System.out.println("移除元素" + blockingQueue.remove());
//        System.out.println("移除元素"+blockingQueue.remove());
    }
}

1.1 正常执行结果:
在这里插入图片描述


1.2 将注释的代码System.out.println("添加元素" + blockingQueue.add("d"));打开。即:
在这里插入图片描述
执行结果:
在这里插入图片描述
报异常:java.lang.IllegalStateException: Queue full


1.3 将注释的代码System.out.println("移除元素" + blockingQueue.remove());打开,即:
在这里插入图片描述
执行结果:
在这里插入图片描述
报异常:Exception in thread “main” java.util.NoSuchElementException


1.4 小结:

  1. 当阻塞队列满时,再往队列里add插入元素会抛出java.lang.IllegalStateException: Queue full异常。
    当阻塞队列空时,再往队列里remove移除元素会抛出Exception in thread “main” java.util.NoSuchElementException异常。

2. 阻塞队列api之返回布尔值组

代码:

public static void main(String[] args) throws InterruptedException {
System.out.println(blockingQueue.offer("a"));
       BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println("添加元素"+blockingQueue.offer("a"));
        System.out.println("添加元素"+blockingQueue.offer("b"));
        System.out.println("添加元素"+blockingQueue.offer("c"));
        System.out.println("添加元素"+blockingQueue.offer("a"));

        System.out.println("队列首个元素:"+blockingQueue.peek());

        System.out.println("移除元素"+blockingQueue.poll());
        System.out.println("移除元素"+blockingQueue.poll());
        System.out.println("移除元素"+blockingQueue.poll());
        System.out.println("移除元素"+blockingQueue.poll());

2.1 执行结果:
在这里插入图片描述
2.2 小结:

  1. 插入方法,成功ture,失败false
  2. 移除方法,成功返回出队列的元素,队列里没有就返回null

3. 阻塞队列api之阻塞

代码:

 public static void main(String[] args) throws InterruptedException {
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
        blockingQueue.put("a");
        blockingQueue.put("a");
        blockingQueue.put("a");
      //blockingQueue.put("a");

        System.out.println("==================");

        blockingQueue.take();
        blockingQueue.take();
        blockingQueue.take();
      //blockingQueue.take();

    }

当打开注释blockingQueue.put("a");或者blockingQueue.take();
执行结果:
在这里插入图片描述
小结:

  1. 当阻塞队列满时,生产者线程继续往队列里put元素,队列会一直阻塞生产线程直到put数据或者响应中断退出。
  2. 当阻塞队列空时,消费者线程试图从队列里take元素,队列会一直阻塞消费者线程直到队列可用。

4. 阻塞队列api之超时控制

代码:

 public static void main(String[] args) throws InterruptedException {
		BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(blockingQueue.offer("a", 2, TimeUnit.SECONDS));
        System.out.println(blockingQueue.offer("a", 2, TimeUnit.SECONDS));
        System.out.println(blockingQueue.offer("a", 2, TimeUnit.SECONDS));
        System.out.println(blockingQueue.offer("a", 2, TimeUnit.SECONDS));

    }

执行结果:

在这里插入图片描述
先阻塞两秒钟,2秒后自动结束


4. SynchronousQueue的用法

  1. 与其他BlockingQueue不同,SynchronousQueue是一个不存储元素的BlockingQueue。每一个put操作必须要等待一个take操作,否则不能继续添加元素,反之亦然。

代码实例:

 public static void main(String[] args) throws Exception {
        BlockingQueue<String> blockingQueue = new SynchronousQueue<>();

        new Thread(() -> {
            try {
                System.out.println(Thread.currentThread().getName()+"\t put 1");
                blockingQueue.put("1");
                System.out.println(Thread.currentThread().getName()+"\t put 2");
                blockingQueue.put("2");
                System.out.println(Thread.currentThread().getName()+"\t put 3");
                blockingQueue.put("3");
            }catch (Exception e){
                e.printStackTrace();
            }
        },"AAA").start();

        new Thread(() -> {
            try {
                //暂停一会线程
                try {
                    TimeUnit.SECONDS.sleep(3);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"\t"+blockingQueue.take());

                try {
                    TimeUnit.SECONDS.sleep(3);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"\t"+blockingQueue.take());

                try {
                    TimeUnit.SECONDS.sleep(3);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"\t"+blockingQueue.take());

            }catch (Exception e){
                e.printStackTrace();
            }
        },"BBB").start();

    }

执行结果:

在这里插入图片描述


5. 用在哪里

主要用在:

  1. 生产者消费者模式(阻塞队列版)
  2. 线程池
  3. 消息中间件

等后面写完了再补上链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值