JavaSE多线程阻塞队列实现代码

 更新时间:2025年12月21日 11:39:34   作者:鸽鸽程序猿  
阻塞队列是一种线程安全的队列,可以用于多线程之间的数据传递和同步,下面这篇文章主要介绍了JavaSE多线程阻塞队列的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下

一、阻塞队列

阻塞队列:是一种特殊的队列,也有先进先出的特性。它是一种线程安全的队列。
有以下两个特性:

  • 当队列的时候, 继续入队列就会阻塞, 直到有其他线程从队列中取走元素。
  • 当队列的时候, 继续出队列也会阻塞, 直到有其他线程往队列中插入元素。
    阻塞队列的一个重要应用场景就是:实现生产者消费模型。

1.1 生产者消费者模型

生产者消费者模型:是多线程编程中的一种典型的编码技巧。用来降低生产者与消费者之间的耦合度。生产者和消费者之间的交易场所就是一个阻塞队列。

这样的模型的优势有以下两个:

  1. 解耦合,降低代码耦合度:
    像如果是A B两个服务器,之间直接进行交互,如果对A或者B中的数据进行修改操作,大概率就会影响到另一个服务器。而使用阻塞队列作为交易平台,我们修改服务器的数据时,由于阻塞队列中的结构固定,两个服务器之间的耦合度就降低。
  2. 削峰削谷:
    在服务器中,波峰就是请求量高的时候,波谷就是请求量低的时候。
    如果是AB两个服务器之间进行交互,当上游服务器A经历波峰,将大量请求传给服务器B的时候,服务器就有可能挂掉。
    因为上游服务器,干的活简单,消耗的资源少;而下游服务器,干的活复杂,消耗的的资源就多。
    但是如果我们将阻塞队列作为交易平台,那么服务器B就可以依据自己的节奏从队列中拿请求。

但是这样的模型也会付出代价:

  1. 引入阻塞队列之后整体结构会更加复杂。比如本来是AB两个服务器之间的交互,但引入一个作为阻塞队列的服务器(这种称为消息队列),就需要部署这个服务器,还要与AB实现交互。
  2. 效率也会有影响。

1.2 Java提供的阻塞队列

提供了一个BlockingDeque的接口(需要导java.util.concurrent.BlockingQueue包):

主要使用下面3个实现了BlockingDeque接口的来实例化阻塞队列:

  1. 链表实现的,LinkedBlockingDeque(需要导java.util.concurrent.LinkedBlockingDeque包):

  2. 数组实现的,ArrayBlockingDeque需要导`java.util.concurrent.ArrayBlockingDeque包):

  3. 小根堆实现的,PriorityBlockingDeque需要导java.util.concurrent.PriorityBlockingDeque包):

在阻塞队列中我们虽然可以使用队列中常用的出队列入队列方法,但是那些方法不带阻塞效果。带阻塞效果的入队列方法是put,出队列方法是take,这两个方法都会抛出InterruptedException异常。

1.3 实现一个简单生产者消费者模型

实现一个简单的生产者消费者模型:

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;

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

        Thread producer = new Thread(() -> {
            int i = 0;
            while(true) {
                try {
                    blockingQueue.put(i++);
                    System.out.println(i + "入队列成功");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        Thread consumer = new Thread(() -> {
            while(true){
                try {
                    int x = blockingQueue.take();
                    System.out.println(x + "出队列成功");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        producer.start();
        consumer.start();
    }
}

二、自己实现阻塞队列

我们使用数组来实现一个循环队列。

不知道循环队列的实现的可以看下面这个链接:队列

2.1 成员变量

  • 使用capacity代表数组的最大长度;
  • 使用size表示数组中元素的个数;
  • head表示队头元素的下标;
  • tail表示队尾元素的下标。
	private int capacity = 0xffff;
    private String[] elem ;//存储数组
    private int size;//存储元素个数
    private int head;//队头
    private int tail;//队尾

2.2 构造方法

提供两个构造方法:

  • 使用默认最大值初始化数组;
  • 使用传的参初始化数组。
    public MyBlockingQueue(int capacity) {
        this.capacity = capacity;
        elem = new String[this.capacity];
    }
    public MyBlockingQueue() {
        elem = new String[this.capacity];
    }

2.3 put方法

由于put和take方法都涉及到修改判断等操作,为避免原子性问题带来线程安全问题对该这些操作都要加锁。

在put方法中我们需要在队列满的时候发生阻塞,使用wait来等待。而在Java官方文档给出了建议我们使用循环语句来使用wait。

因为wait是除了notify唤醒外,还有可能被interrupt方法唤醒抛出异常,如果只要if,不用while,抛出异常后就会继续执行下面的逻辑,带来bug。而使用循环就不会,抛出异常后,会再次判断循环条件。

最后在入队成功后发出一个通知notify来唤醒由于队列空而阻塞等待的线程。

    public void put(String s) throws InterruptedException {
        synchronized (this) {

            while(size == elem.length) {
                this.wait();
            }
            elem[tail] = s;
            tail = (tail+1) % elem.length;
            size++;
            this.notify();
        }
    }

2.4 take方法

当队列为空的时候,跟put一样使用wait来阻塞。

最后在出队成功后发出一个通知notify来唤醒由于队列满而阻塞等待的线程。

 public String take() throws InterruptedException {
        synchronized (this) {

            while(size == 0) {
                this.wait();
            }
            String ret = elem[head];
            head = (head+1) % capacity;
            size--;
            this.notify();
            return ret;
        }
    }

2.5 最终代码

最终我们自己实现的一个简单的阻塞队列就如下:

public class MyBlockingQueue {
    private int capacity = 0xffff;
    private String[] elem ;//存储数组
    private int size;//存储元素个数
    private int head;//队头
    private int tail;//队尾


    public MyBlockingQueue(int length) {
        elem = new String[length];
    }
    public MyBlockingQueue() {
        elem = new String[this.capacity];
    }

    public void put(String s) throws InterruptedException {
        synchronized (this) {

            while(size == elem.length) {
                this.wait();
            }
            elem[tail] = s;
            tail = (tail+1) % capacity;
            size++;
            this.notify();
        }
    }

    public String take() throws InterruptedException {
        synchronized (this) {

            while(size == 0) {
                this.wait();
            }
            String ret = elem[head];
            head = (head+1) % capacity;
            size--;
            this.notify();
            return ret;
        }
    }
}

总结 

到此这篇关于JavaSE多线程阻塞队列实现的文章就介绍到这了,更多相关JavaSE多线程阻塞队列内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java调用python脚本引入第三方库失败的实现

    java调用python脚本引入第三方库失败的实现

    本文主要介绍了java调用python脚本引入第三方库失败的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-07-07
  • java后台调用HttpURLConnection类模拟浏览器请求实例(可用于接口调用)

    java后台调用HttpURLConnection类模拟浏览器请求实例(可用于接口调用)

    这篇文章主要介绍了java后台调用HttpURLConnection类模拟浏览器请求实例,该实例可用于接口调用,具有一定的实用价值,需要的朋友可以参考下
    2014-10-10
  • 浅谈springMVC拦截器和过滤器总结

    浅谈springMVC拦截器和过滤器总结

    本篇文章主要介绍了springMVC拦截器和过滤器总结,可以用来对访问的url进行拦截处理,有兴趣的可以了解一下。
    2017-01-01
  • Java利用Netty时间轮实现延时任务

    Java利用Netty时间轮实现延时任务

    时间轮是一种可以执行定时任务的数据结构和算法。本文将为大家详细讲解一下Java如何利用Netty时间轮算法实现延时任务,感兴趣的小伙伴可以了解一下
    2022-08-08
  • SpringBoot+mybatis+thymeleaf实现登录功能示例

    SpringBoot+mybatis+thymeleaf实现登录功能示例

    这篇文章主要介绍了SpringBoot+mybatis+thymeleaf实现登录功能示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07
  • Java实现短信验证码功能的完整代码

    Java实现短信验证码功能的完整代码

    这篇文章主要为大家详细介绍了Java实现短信验证码功能的完整代码,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以了解下
    2025-12-12
  • SpringMVC的异常处理和拦截器处理思路解析

    SpringMVC的异常处理和拦截器处理思路解析

    这篇文章介绍了SpringMVC的异常处理和拦截器功能,在异常处理部分,详细说明了如何通过DispatcherServlet捕获并处理异常,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2025-09-09
  • Spring Security 中细化权限粒度的方法

    Spring Security 中细化权限粒度的方法

    这篇文章主要介绍了Spring Security 中细化权限粒度的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-09-09
  • RocketMQ 源码分析Broker消息刷盘服务

    RocketMQ 源码分析Broker消息刷盘服务

    这篇文章主要为大家介绍了RocketMQ 源码分析Broker消息刷盘服务示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-05-05
  • SpringBoot2整合JTA组件实现多数据源事务管理

    SpringBoot2整合JTA组件实现多数据源事务管理

    这篇文章主要介绍了SpringBoot2整合JTA组件实现多数据源事务管理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03

最新评论