Java LinkedBlockingQueue 实现

简介: 本文着重介绍 Java 并发容器中 LinkedBlockingQueue 的实现方式。

引言

本文着重介绍 Java 并发容器中 LinkedBlockingQueue 的实现方式。所有关于 Java 并发的文章均收录于<Java并发系列文章>

LinkedBlockingQueue

LinkedBlockingQueue 底层基于单向链表实现的阻塞队列,可以当做无界队列也可以当做有界队列来使用,满足FIFO的特性,为了防止 LinkedBlockingQueue 容量迅速增,损耗大量内存。通常在创建LinkedBlockingQueue 对象时,会指定其大小,如果未指定,容量等于Integer. MAX_VALUE。那么什么是阻塞队列呢?我们知道队列有入队出队两个操作,所谓阻塞队列,就是说如果队列已满时,可以阻塞入队操作,而如果队列为空时,可以阻塞出队操作。

为了实现阻塞效果并保证线程安全,它的内部用到了两个锁和两个Condition。

/** Lock held by take, poll, etc */
private final ReentrantLock takeLock = new ReentrantLock();

/** Wait queue for waiting takes */
private final Condition notEmpty = takeLock.newCondition();

/** Lock held by put, offer, etc */
private final ReentrantLock putLock = new ReentrantLock();

/** Wait queue for waiting puts */
private final Condition notFull = putLock.newCondition();
AI 代码解读

在进行数据出队时,先要获得 takeLock,然后检查当前队列容量是否为 0,如果队列容量为 0,则在 notEmpty 上等待,否则直接执行出队操作。最后判断一下,是不是执行出队操作之前,队列已经达到最大容量,如果是的话,就唤醒等待中的入队操作。

public E take() throws InterruptedException {
    E x;
    int c = -1;
    final AtomicInteger count = this.count;
    // 获取锁
    final ReentrantLock takeLock = this.takeLock;
    takeLock.lockInterruptibly();
    try {
        // 如果队列为空,就等待
        while (count.get() == 0) {
            notEmpty.await();
        }
        // 否则,执行出队操作,并修改 size
        x = dequeue();
        c = count.getAndDecrement();
        if (c > 1)
            // 如果队列不为空,则唤醒下一个等待中的出队操作
            notEmpty.signal();
    } finally {
        takeLock.unlock();
    }
    // 如果之前队列满了,则唤醒等待中的入队操作
    if (c == capacity)
        signalNotFull();
    return x;
}

/**
 * Signals a waiting put. Called only from take/poll.
 */
private void signalNotFull() {
    final ReentrantLock putLock = this.putLock;
    putLock.lock();
    try {
        notFull.signal();
    } finally {
        putLock.unlock();
    }
}
AI 代码解读

入队操作和出队操作正好相反,同样先获取锁,不过这里用的是 putLock,检查当前队列是否已满,是的话就在 notFull 上等待,否则执行入队操作并修改size,如果之前的队列长度为 0,那么就有可能有一些出队操作被阻塞了,所以我们这里需要唤醒所有在 notEmpty 上等待的线程。

/**
 * Inserts the specified element at the tail of this queue, waiting if
 * necessary for space to become available.
 *
 * @throws InterruptedException {@inheritDoc}
 * @throws NullPointerException {@inheritDoc}
 */
public void put(E e) throws InterruptedException {
    if (e == null) throw new NullPointerException();
    // Note: convention in all put/take/etc is to preset local var
    // holding count negative to indicate failure unless set.
    int c = -1;
    Node<E> node = new Node<E>(e);
    final ReentrantLock putLock = this.putLock;
    final AtomicInteger count = this.count;
    // 先获取锁
    putLock.lockInterruptibly();
    try {
        /*
         * Note that count is used in wait guard even though it is
         * not protected by lock. This works because count can
         * only decrease at this point (all other puts are shut
         * out by lock), and we (or some other waiting put) are
         * signalled if it ever changes from capacity. Similarly
         * for all other uses of count in other wait guards.
         */
        while (count.get() == capacity) {
            notFull.await();
        }
        enqueue(node);
        c = count.getAndIncrement();
        if (c + 1 < capacity)
            notFull.signal();
    } finally {
        putLock.unlock();
    }
    if (c == 0)
        signalNotEmpty();
}

/**
 * Signals a waiting take. Called only from put/offer (which do not
 * otherwise ordinarily lock takeLock.)
 */
private void signalNotEmpty() {
    final ReentrantLock takeLock = this.takeLock;
    takeLock.lock();
    try {
        notEmpty.signal();
    } finally {
        takeLock.unlock();
    }
}
AI 代码解读

LinkedBlockingQueue 除了阻塞版的入队出队操作外,当然也有不阻塞的接口,不过这些接口比较简单,基本上就是在上述基础取消 await 和 signal 逻辑,这里就不再赘述了。

文章说明

更多有价值的文章均收录于贝贝猫的文章目录

stun

版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

创作声明: 本文基于下列所有参考内容进行创作,其中可能涉及复制、修改或者转换,图片均来自网络,如有侵权请联系我,我会第一时间进行删除。

参考内容

[1] linux 2.6 互斥锁的实现-源码分析
[2] 深入解析条件变量(condition variables)
[3] Linux下Condition Vairable和Mutext合用的小细节
[4] 从ReentrantLock的实现看AQS的原理及应用
[5] 不可不说的Java“锁”事
[6] 从源码层面解析yield、sleep、wait、park
[7] LockSupport中的park与unpark原理
[8] Thread.sleep、Object.wait、LockSupport.park 区别
[9] 从AQS到futex-二-JVM的Thread和Parker
[10] Java的LockSupport.park()实现分析
[11] JVM源码分析之Object.wait/notify实现
[12] Java线程源码解析之interrupt
[13] Thread.interrupt()相关源码分析%E7%9B%B8%E5%85%B3%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/)
[14] Java CAS 原理剖析
[15] 源码解析 Java 的 compareAndSwapObject 到底比较的是什么
[16] 《Java并发编程的艺术》
[17] 《实战 Java 高并发程序设计》
[18] volatile关键字深入学习
[19] 为什么Netty的FastThreadLocal速度快
[20] 线程池ThreadPoolExecutor实现原理
[21] 深入理解Java线程池:ThreadPoolExecutor
[22] ConcurrentHashMap 详解一
[23] ConcurrentHashMap 详解二
[24] JUC中Atomic class之lazySet的一点疑惑
[25] The JSR-133 Cookbook for Compiler Writers
[26] 就是要你懂Java中volatile关键字实现原理

目录
打赏
0
0
0
0
576
分享
相关文章
|
3月前
|
【源码】【Java并发】【LinkedBlockingQueue】适合中学体质的LinkedBlockingQueue入门
前言 有了前文对简单实用的学习 【Java并发】【LinkedBlockingQueue】适合初学体质的LinkedBlockingQueue入门 聪明的你,一定会想知道更多。哈哈哈哈哈,下面主播就...
71 6
【源码】【Java并发】【LinkedBlockingQueue】适合中学体质的LinkedBlockingQueue入门
|
3月前
|
【Java并发】【LinkedBlockingQueue】适合初学体质的LinkedBlockingQueue入门
前言 你是否在线程池工具类里看到过它的身影? 你是否会好奇LinkedBlockingQueue是啥呢? 没有关系,小手手点上关注,跟上主播的节奏。 什么是LinkedBlockingQueue? ...
92 1
【Java并发】【LinkedBlockingQueue】适合初学体质的LinkedBlockingQueue入门
Java线程池ThreadPoolExcutor源码解读详解03-阻塞队列之LinkedBlockingQueue
LinkedBlockingQueue 和 ArrayBlockingQueue 是 Java 中的两种阻塞队列实现,它们的主要区别在于: 1. **数据结构**:ArrayBlockingQueue 采用固定大小的数组实现,而 LinkedBlockingQueue 则使用链表实现。 2. **容量**:ArrayBlockingQueue 在创建时必须指定容量,而 LinkedBlockingQueue 可以在创建时不指定容量,默认容量为 Integer.MAX_VALUE。 总结起来,如果需要高效并发且内存不是主要考虑因素,LinkedBlockingQueue 通常是更好的选择;
269 1
Java并发基础:LinkedBlockingQueue全面解析!
LinkedBlockingQueue类是以链表结构实现高效线程安全队列,具有出色的并发性能、灵活的阻塞与非阻塞操作,以及适用于生产者和消费者模式的能力,此外,LinkedBlockingQueue还具有高度的可伸缩性,能够在多线程环境中有效管理数据共享,是提升程序并发性能和稳定性的关键组件。
258 0
Java并发基础:LinkedBlockingQueue全面解析!
Java 实现汉字按照首字母分组排序
Java 实现汉字按照首字母分组排序
782 0
Java网络编程:UDP/TCP实现实时聊天、上传图片、下载资源等
ip地址的分类: 1、ipv4、ipv6 127.0.0.1:4个字节组成,0-255,42亿;30亿都在北美,亚洲就只有4亿 2011年就用尽了。
Java网络编程:UDP/TCP实现实时聊天、上传图片、下载资源等
Java LinkedBlockingQueue实现消息队列
最近有个项目需要开发一个预约系统,系统涉及到发送短信验证码;一般用户点击发送验证码,发送请求到后端后,调用短信接口,成功后就返回响应的状态码给用户;但是这样的过程,有时候会因为短信接口响应慢,而导致前端响应慢;所以这里需要做一个简单的优化,当用户点击发送短信时,将我们的短信调用放入一个队列中,放入之后,即给前端响应;后面通过阻塞队列,取出队列内容,进行短信发送即可,这样可以更好的提升系统的性能和用户体验度;
199 0
Java Review - 并发编程_LinkedBlockingQueue原理&源码剖析
Java Review - 并发编程_LinkedBlockingQueue原理&源码剖析
141 0
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问