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

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

3.3、public void put(E e) throws InterruptedException

原理:

  • 在队尾插入一个元素,如果队列满了,一直阻塞,直到数组不满了或者线程被中断

使用方法:

        try {
            abq.put("hello1");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

源代码:

    /**
     * 在队尾插入一个元素
     * 如果队列满了,一直阻塞,直到数组不满了或者线程被中断
     */
    public void put(E e) throws InterruptedException {
        if (e == null)
            throw new NullPointerException();
        final E[] items = this.items;
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            try {
                while (count == items.length)//队列满了,一直阻塞在这里
                    /*
                     * 一直等待条件notFull,即被其他线程唤醒
                     * (唤醒其实就是,有线程将一个元素出队了,然后调用notFull.signal()唤醒其他等待这个条件的线程,同时队列也不慢了)
                     */
                    notFull.await();
            } catch (InterruptedException ie) {//如果被中断
                notFull.signal(); // 唤醒其他等待该条件(notFull,即入队)的线程
                throw ie;
            }
            insert(e);
        } finally {
            lock.unlock();
        }
    }

4、出队

4.1、public E poll()

原理:

  • 如果没有元素,直接返回null;如果有元素,将队头元素置null,但是要注意队头是随时变化的,并非一直是items[0]。

使用方法:

abq.poll();

源代码:

    /**
     * 出队
     */
    public E poll() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            if (count == 0)//如果没有元素,直接返回null,而非抛出异常
                return null;
            E x = extract();
            return x;
        } finally {
            lock.unlock();
        }
    }
    /**
     * 出队
     */
    private E extract() {
        final E[] items = this.items;
        E x = items[takeIndex];//获取出队元素
        items[takeIndex] = null;//将出队元素位置置空
        /*
         * 第一次出队的元素takeIndex==0,第二次出队的元素takeIndex==1
         * (注意:这里出队之后,并没有将后面的数组元素向前移)
         */
        takeIndex = inc(takeIndex);
        --count;//数组元素个数-1
        notFull.signal();//数组已经不满了,唤醒其他等待notFull条件的线程
        return x;//返回出队的元素
    }

4.2、public E poll(long timeout, TimeUnit unit) throws InterruptedException

原理:

  • 从对头删除一个元素,如果数组不空,出队;如果数组已空且已经超时,返回null;如果数组已空且时间未超时,则进入等待,直到出现以下三种情况:

    • 被唤醒

    • 等待时间超时

    • 当前线程被中断

使用方法:

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

源代码:

    /**
     * 从对头删除一个元素,
     * 如果数组不空,出队;
     * 如果数组已空,判断时间是否超时,如果已经超时,返回null
     * 如果数组已空且时间未超时,则进入等待,直到出现以下三种情况:
     * 1、被唤醒
     * 2、等待时间超时
     * 3、当前线程被中断
     */
    public E poll(long timeout, TimeUnit unit) throws InterruptedException {
        long nanos = unit.toNanos(timeout);//将时间转换为纳秒
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            for (;;) {
                if (count != 0) {//数组不空
                    E x = extract();//出队
                    return x;
                }
                if (nanos <= 0)//时间超时
                    return null;
                try {
                    /*
                     * 进行等待:
                     * 在这个过程中可能发生三件事:
                     * 1、被唤醒-->继续当前这个for(;;)循环
                     * 2、超时-->继续当前这个for(;;)循环
                     * 3、被中断-->之后直接执行catch部分的代码
                     */
                    nanos = notEmpty.awaitNanos(nanos);
                } catch (InterruptedException ie) {
                    notEmpty.signal(); // propagate to non-interrupted thread
                    throw ie;
                }             }
        } finally {
            lock.unlock();
        }
    }

4.3、public E take() throws InterruptedException

原理:

  • 将队头元素出队,如果队列空了,一直阻塞,直到数组不为空或者线程被中断

使用方法:

        try {
            abq.take();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

源代码:

    /**
     * 将队头元素出队
     * 如果队列空了,一直阻塞,直到数组不为空或者线程被中断
     */
    public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            try {
                while (count == 0)//如果数组为空,一直阻塞在这里
                    /*
                     * 一直等待条件notEmpty,即被其他线程唤醒
                     * (唤醒其实就是,有线程将一个元素入队了,然后调用notEmpty.signal()唤醒其他等待这个条件的线程,同时队列也不空了)
                     */
                    notEmpty.await();
            } catch (InterruptedException ie) {
                notEmpty.signal(); // propagate to non-interrupted thread
                throw ie;
            }
            E x = extract();
            return x;
        } finally {
            lock.unlock();
        }
    }

总结:

1、具体入队与出队的原理图:这里只说一种情况,见下图,途中深色部分表示已有元素,浅色部分没有元素。

上面这种情况是怎么形成的呢?当队列满了,这时候,队头元素为items[0]出队了,就形成上边的这种情况。

假设现在又要出队了,则现在的队头元素是items[1],出队后就形成下面的情形。

出队后,对头元素就是items[2]了,假设现在有一个元素将要入队,根据inc方法,我们可以得知,他要插入到items[0]去,入队了形成下图:

以上就是整个入队出队的流程,inc方法上边已经给出,这里再贴一遍:

    /**
     * i+1,数组下标+1
     * 注意:这里这样写的原因。
     */
    final int inc(int i) {
        return (++i == items.length) ? 0 : i;
    }

2、三种入队对比:

  • offer(E e):如果队列没满,立即返回true; 如果队列满了,立即返回false-->不阻塞

  • put(E e):如果队列满了,一直阻塞,直到数组不满了或者线程被中断-->阻塞

  • offer(E e, long timeout, TimeUnit unit):在队尾插入一个元素,,如果数组已满,则进入等待,直到出现以下三种情况:-->阻塞

    • 被唤醒

    • 等待时间超时

    • 当前线程被中断

3、三种出对对比:

  • poll():如果没有元素,直接返回null;如果有元素,出队

  • take():如果队列空了,一直阻塞,直到数组不为空或者线程被中断-->阻塞

  • poll(long timeout, TimeUnit unit):如果数组不空,出队;如果数组已空且已经超时,返回null;如果数组已空且时间未超时,则进入等待,直到出现以下三种情况:

    • 被唤醒

    • 等待时间超时

    • 当前线程被中断

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

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

相关文章:
【推荐】 深入解析SQLServer高可用镜像实现原理

ArrayBlockingQueue源码解析(2)的更多相关文章

  1. ArrayBlockingQueue源码解析(1)

    此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 注意:在阅读本文之前或在阅读的过程中,需要用到ReentrantLock,内容见<第五章 Reentr ...

  2. 第八章 ArrayBlockingQueue源码解析

    注意:在阅读本文之前或在阅读的过程中,需要用到ReentrantLock,内容见<第五章 ReentrantLock源码解析1--获得非公平锁与公平锁lock()><第六章 Reen ...

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

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

  4. Java源码解析——集合框架(二)——ArrayBlockingQueue

    ArrayBlockingQueue源码解析 ArrayBlockingQueue是一个阻塞式的队列,继承自AbstractBlockingQueue,间接的实现了Queue接口和Collection ...

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

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

  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. java Mather 的 group 含义

    参考博文:  http://blog.csdn.net/java2king/article/details/4395067  明白了group 的 含义 public class Test { pub ...

  2. C#动态显示时间

    private void timer1_Tick(object sender, EventArgs e) { DateTime dt = DateTime.Now; label1.Text = dt. ...

  3. Linux学习笔记:Shell脚本学习

    概念 真正能够控制计算机硬件(CPU.内存.显示器等)的只有操作系统内核(Kernel),图形界面和命令行只是架设在用户和内核之间的一座桥梁. 由于安全.复杂.繁琐等原因,用户不能直接接触内核(也没有 ...

  4. Windows-universal-samples学习笔记系列四:Data

    Data Blobs Compression Content indexer Form validation (HTML) IndexedDB Logging Serializing and dese ...

  5. python之并发编程进阶篇9

    一.守护进程和守护线程 1)守护进程的概念 什么是守护进程: 守护: 在主进程代码结束情况下,就立即死掉 守护进程本质就是一个子进程,该子进程守护着主进程 为何要用守护进程 守护进程本质就是一个子进程 ...

  6. 初识python函数

    一.函数 1.什么是函数 函数是对功能或者动作的封装 2.函数的语法和定义 def 函数名(): 函数体 调用: 函数名() 3.关于函数的返回值 return :  返回 1.当程序没写过retur ...

  7. oracle 锁表查看与解锁

    查看被锁的表: select l.session_id,o.owner,o.object_name from v$locked_object l,dba_objects o where l.objec ...

  8. nginx反向代理缓存服务器的构建

    一:代理服务可简单的分为正向代理和反向代理: 正向代理:用于代理内部网络对Internet的连接请求(如VPN/NAT),客户端指定代理服务器,并将本来要直接发送给目标Web服务器的HTTP请求先发送 ...

  9. 2018.11.09 bzoj2165: 大楼(倍增+floyd)

    传送门 先倍增出iii使得2i2^i2i时间时刚好有每个点能够到mmm层及以上. 然后就可以用floyd+floyd+floyd+倍增求出刚好不超过mmm层的时间,最后再补一层就行了. 代码: #pr ...

  10. 使用hibernate从一方获取多方信息时报错:org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role

    引起原因:hibernate加载关联对象的方式有懒加载方式和立即加载方式. 如果在多对一的配置中没有指定加载方式,而一对多的配置中指定了懒加载方式,因此在获取一方是可获取到值,而获取多方时sessio ...