此文已由作者赵计刚授权网易云社区发布。

欢迎访问网易云社区,了解更多网易技术产品运营经验。


1、对于LinkedBlockingQueue需要掌握以下几点

  • 创建

  • 入队(添加元素)

  • 出队(删除元素)

2、创建

Node节点内部类与LinkedBlockingQueue的一些属性

    static class Node<E> {
        E item;//节点封装的数据
        /**
         * One of:
         * - the real successor Node
         * - this Node, meaning the successor is head.next
         * - null, meaning there is no successor (this is the last node)
         */         Node<E> next;//下一个节点
        Node(E x) { item = x; }
    }     /** 指定链表容量  */
    private final int capacity;     /** 当前的元素个数 */
    private final AtomicInteger count = new AtomicInteger(0);     /** 链表头节点 */
    private transient Node<E> head;     /** 链表尾节点 */
    private transient Node<E> last;     /** 出队锁 */
    private final ReentrantLock takeLock = new ReentrantLock();     /** 出队等待条件 */
    private final Condition notEmpty = takeLock.newCondition();     /** 入队锁 */
    private final ReentrantLock putLock = new ReentrantLock();     /** 入队等待条件 */
    private final Condition notFull = putLock.newCondition();

2.1、public LinkedBlockingQueue(int capacity)

使用方法:

Queue<String> abq = new LinkedBlockingQueue<String>(1000);

源代码:

    /**
     * 创建一个 LinkedBlockingQueue,容量为指定容量
     */
    public LinkedBlockingQueue(int capacity) {
        if (capacity <= 0) throw new IllegalArgumentException();
        this.capacity = capacity;
        last = head = new Node<E>(null);//初始化头节点和尾节点,均为封装了null数据的节点
    }

注意点:

  • LinkedBlockingQueue的组成一个链表+两把锁+两个条件

2.2、public LinkedBlockingQueue()

使用方法:

Queue<String> abq = new LinkedBlockingQueue<String>();

源代码:

    /**
     * 创建一个LinkedBlockingQueue,容量为整数最大值
     */
    public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }

注意点:默认容量为整数最大值,可以看做没有容量限制

3、入队:

3.1、public boolean offer(E e)

原理:

  • 在队尾插入一个元素, 如果队列没满,立即返回true; 如果队列满了,立即返回false

使用方法:

  • abq.offer("hello1");

源代码:

    /**
     * 在队尾插入一个元素, 容量没满,可以立即插入,返回true; 队列满了,直接返回false
     * 注:如果使用了限制了容量的队列,这个方法比add()好,因为add()插入失败就会抛出异常
     */
    public boolean offer(E e) {
        if (e == null)
            throw new NullPointerException();
        final AtomicInteger count = this.count;// 获取队列中的元素个数
        if (count.get() == capacity)// 队列满了
            return false;
        int c = -1;
        final ReentrantLock putLock = this.putLock;
        putLock.lock();// 获取入队锁
        try {
            if (count.get() < capacity) {// 容量没满
                enqueue(e);// 入队
                c = count.getAndIncrement();// 容量+1,返回旧值(注意)
                if (c + 1 < capacity)// 如果添加元素后的容量,还小于指定容量(说明在插入当前元素后,至少还可以再插一个元素)
                    notFull.signal();// 唤醒等待notFull条件的其中一个线程
            }
        } finally {
            putLock.unlock();// 释放入队锁
        }
        if (c == 0)// 如果c==0,这是什么情况?一开始如果是个空队列,就会是这样的值,要注意的是,上边的c返回的是旧值
            signalNotEmpty();
        return c >= 0;
    }
    /**
     * 创建一个节点,并加入链表尾部
     * @param x
     */
    private void enqueue(E x) {
        /*
         * 封装新节点,并赋给当前的最后一个节点的下一个节点,然后在将这个节点设为最后一个节点
         */
        last = last.next = new Node<E>(x);
    }
    private void signalNotEmpty() {
        final ReentrantLock takeLock = this.takeLock;
        takeLock.lock();//获取出队锁
        try {
            notEmpty.signal();//唤醒等待notEmpty条件的线程中的一个
        } finally {
            takeLock.unlock();//释放出队锁
        }
    }

如果,入队逻辑不懂,查看最后总结部分入队逻辑的图,代码非常简单,流程看注释即可。

3.2、public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException

原理:

  • 在队尾插入一个元素,,如果队列已满,则进入等待,直到出现以下三种情况:

    • 被唤醒

    • 等待时间超时

    • 当前线程被中断

使用方法:

        try {
            abq.offer("hello2",1000,TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

源代码:

    /**
     * 在队尾插入一个元素,,如果队列已满,则进入等待,直到出现以下三种情况: 
     * 1、被唤醒 
     * 2、等待时间超时 
     * 3、当前线程被中断
     */
    public boolean offer(E e, long timeout, TimeUnit unit)
            throws InterruptedException {         if (e == null)
            throw new NullPointerException();
        long nanos = unit.toNanos(timeout);// 转换为纳秒
        int c = -1;
        final ReentrantLock putLock = this.putLock;// 入队锁
        final AtomicInteger count = this.count;// 总数量
        putLock.lockInterruptibly();
        try {
            while (count.get() == capacity) {// 容量已满
                if (nanos <= 0)// 已经超时
                    return false;
                /*
                 * 进行等待: 在这个过程中可能发生三件事: 
                 * 1、被唤醒-->继续当前这个while循环
                 * 2、超时-->继续当前这个while循环 
                 * 3、被中断-->抛出中断异常InterruptedException
                 */
                nanos = notFull.awaitNanos(nanos);
            }
            enqueue(e);// 入队
            c = count.getAndIncrement();// 入队元素数量+1
            if (c + 1 < capacity)
                notFull.signal();
        } finally {
            putLock.unlock();
        }
        if (c == 0)
            signalNotEmpty();
        return true;
    }

注意:

  • awaitNanos(nanos)是AQS中的一个方法,这里就不详细说了,有兴趣的自己去查看AQS的源代码。

免费领取验证码、内容安全、短信发送、直播点播体验包及云服务器等套餐

更多网易技术、产品、运营经验分享请点击

相关文章:
【推荐】 LESS+to+MCSS
【推荐】 BRVAH(让RecyclerView变得更高效) (2)
【推荐】 微服务化之缓存的设计

LinkedBlockingQueue源码解析(1)的更多相关文章

  1. LinkedBlockingQueue源码解析

    上一篇博客,我们介绍了ArrayBlockQueue,知道了它是基于数组实现的有界阻塞队列,既然有基于数组实现的,那么一定有基于链表实现的队列了,没错,当然有,这就是我们今天的主角:LinkedBlo ...

  2. LinkedBlockingQueue源码解析(3)

    此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 4.3.public E take() throws InterruptedException 原理: 将队 ...

  3. 第九章 LinkedBlockingQueue源码解析

    1.对于LinkedBlockingQueue需要掌握以下几点 创建 入队(添加元素) 出队(删除元素) 2.创建 Node节点内部类与LinkedBlockingQueue的一些属性 static ...

  4. Java并发包源码学习系列:阻塞队列实现之LinkedBlockingQueue源码解析

    目录 LinkedBlockingQueue概述 类图结构及重要字段 构造器 出队和入队操作 入队enqueue 出队dequeue 阻塞式操作 E take() 阻塞式获取 void put(E e ...

  5. LinkedBlockingQueue源码解析(2)

    此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 3.3.public void put(E e) throws InterruptedException 原 ...

  6. Java并发包源码学习系列:阻塞队列实现之PriorityBlockingQueue源码解析

    目录 PriorityBlockingQueue概述 类图结构及重要字段 什么是二叉堆 堆的基本操作 向上调整void up(int u) 向下调整void down(int u) 构造器 扩容方法t ...

  7. Java并发包源码学习系列:阻塞队列实现之DelayQueue源码解析

    目录 DelayQueue概述 类图及重要字段 Delayed接口 Delayed元素案例 构造器 put take first = null 有什么用 总结 参考阅读 系列传送门: Java并发包源 ...

  8. Java并发包源码学习系列:阻塞队列实现之SynchronousQueue源码解析

    目录 SynchronousQueue概述 使用案例 类图结构 put与take方法 void put(E e) E take() Transfer 公平模式TransferQueue QNode t ...

  9. Java并发包源码学习系列:阻塞队列实现之LinkedTransferQueue源码解析

    目录 LinkedTransferQueue概述 TransferQueue 类图结构及重要字段 Node节点 前置:xfer方法的定义 队列操作三大类 插入元素put.add.offer 获取元素t ...

随机推荐

  1. iOS 开发 需要的版本管理工具,UI图,bug管理工具等

    1.版本管理工具  或直接 终端敲命令SVN(smartSvn 或者cornerstone/终端)  或git (sourceTree/终端) 2. 原型管理工具 使用墨刀(https://modao ...

  2. dede后台一片空白

    原因是你修改了后台的数据库连接信息文件data/common.inc.php,保存的后文件编码并不是utf-8,而是变成了ANSI或utf-8 + bom的. 解决方法: 用editplus或note ...

  3. Html的Padding,Margin自己理解图

    Html的Padding,Margin自己理解图.

  4. 获取验证码效果和后台代码(js+html+cs)

    客户端js+html代码 <script type="text/javascript"> var tcode = 0;//定时器返回代码 //获得验证码 functio ...

  5. 【转】MEF程序设计指南三:MEF中组合部件(Composable Parts)与契约(Contracts)的基本应用

    按照MEF的约定,任何一个类或者是接口的实现都可以通过[System.ComponentModel.Composition.ExportAttribute] 特性将其定义为组合部件(Composabl ...

  6. XAMPP Apache + MariaDB + PHP + Perl

    https://www.apachefriends.org/zh_cn/index.html 什么是XAMPP? XAMPP是最流行的PHP开发环境 XAMPP是完全免费且易于安装的Apache发行版 ...

  7. 2018.10.04 NOIP模拟 K进制(模拟)

    传送门 签到题,直接瞎模拟就行了. 代码

  8. 批量 truncate 表

    如果某个用户下所有表或指定表中所有的数据已确定不再需要,此时可以进行批量 truncate declare cursor cur_trunc is select table_name from use ...

  9. 玩转git分支

    搞个代码的管理工具,居然不弄上分支啥的东西.这简直太low了.尤其是在使用了传说中得很牛X的Git的时候,尤其显得low.拿着青龙偃月刀当烧火棍子使,关公知道了还不重反人间教育你!? 远程分支 要说分 ...

  10. Codeforces807 A. Is it rated? 2017-05-08 23:03 177人阅读 评论(0) 收藏

    A. Is it rated? time limit per test 2 seconds memory limit per test 256 megabytes input standard inp ...