前言:

ArrayBlockingQueue类是一个堵塞队列。重要用于多线程操作的条件。

一,官方解释


一个建立在数组之上被BlockingQueue绑定的堵塞队列。这个队列元素顺序是先进先出。队列的头部是在队列中待的时间最长的元素。队列的尾部是再队列中待的时间最短的元素。新的元素会被插入到队列尾部,而且队列从队列头部获取元素。

这是一个典型的绑定缓冲,在这个缓冲区中。有一个固定大小的数组持有生产者插入的数据。而且消费者会提取这些数据。一旦这个类被创建,那么这个数组的容量将不能再被改变。尝试使用put操作给一个满队列插入元素将导致这个操作被堵塞;尝试从空队列中取元素也会被堵塞。

这个类推荐了一个可选的公平策略来排序等待的生产者和消费者线程。

默认的,这个顺序是不确定的。可是队列会使用公平的设置true来使线程依照先进先出顺序訪问。通常公平性会降低吞吐量可是却降低了可变性以及避免了线程饥饿。

这个类和它的迭代器实现了全部可选的Collection按Iterator接口的方法。

​二。源代码分析


先来看看ArrayBlockingQueue类的特殊的一个构造器代码:

public ArrayBlockingQueue(int capacity, boolean fair) {
        if (capacity <= 0)
            throw new IllegalArgumentException();
        this.items = new Object[capacity];
        lock = new ReentrantLock(fair);
        notEmpty = lock.newCondition();
        notFull =  lock.newCondition();
    }

这个构造器中得參数fair大家注意到没有。正好是传给ReentrantLock的參数,而这个參数正好就是ReentrantLock决定是否为公平或者非公平队列的參数。ReentrantLock參考我的这篇关于java中ReentrantLock类的源代码分析以及总结与样例

再往下看到,这两个notEmpty和notFull參数实际上是Condition。而Condition能够把它看做一个堵塞信号,Condition的子类ConditionObject(是AbstractQueuedSynchronizer的内部类)拥有两个方法signal和signalAll方法。前一个方法是唤醒队列中得第一个线程,而signalAll是唤醒队列中得全部等待线程,可是仅仅有一个等待的线程会被选择,这两个方法能够看做notify和notifyAll的变体。

在这个堵塞队列的insert和remove方法中都会被调用signal来唤醒等待线程,在put方法中。假设队列已经满了,则会调用await方法来。直到队列有空位,才会调用insert方法插入元素。源码例如以下:

 public void put(E e) throws InterruptedException {
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == items.length)
                notFull.await();
            insert(e);
        } finally {
            lock.unlock();
        }
    }
private void insert(E x) {
        items[putIndex] = x;
        putIndex = inc(putIndex);
        ++count;
        notEmpty.signal();
    }

事实上设计这个类的程序猿哥哥已经想到了假设我们要用这个类,可是不想在队列满了之后。再插入元素被堵塞。提供了offer方法。这个offer方法有重载方法,调用offer(E e)方法时,假设队列已经满了,那么会直接返回一个false,假设没有满,则直接调用insert插入到队列中;调用offer(E e, long timeout, TimeUnit unit)方法时,会在队列满了之后堵塞队列,可是这里能够由开发者设置超时时间,假设超时时队列还是满的,则会以false返回。源代码例如以下所看到的:

public boolean offer(E e, long timeout, TimeUnit unit)
        throws InterruptedException {         checkNotNull(e);
        long nanos = unit.toNanos(timeout);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == items.length) {
                if (nanos <= 0)
                    return false;
                nanos = notFull.awaitNanos(nanos);
            }
            insert(e);
            return true;
        } finally {
            lock.unlock();
        }
    }
public boolean offer(E e) {
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            if (count == items.length)
                return false;
            else {
                insert(e);
                return true;
            }
        } finally {
            lock.unlock();
        }
    }

插入数据有堵塞和非堵塞之分,那么提取数据也肯定就有堵塞与非堵塞之分了。

当中take方法是个堵塞方法,当队列为空时,就被堵塞。源代码例如以下:

 public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == 0)
                notEmpty.await();
            return extract();
        } finally {
            lock.unlock();
        }
    }

方法poll是重载方法,跟offer相对,也有基础方法和超时方法之分。

在这个类中还提供了peek方法来提取数据。可是peek方法是从对了的tail提取,而pool是从队列的head提取,即peek提取的元素是进入队列最晚的,而pool提取的元素是进入队列最早时间最长的元素。

再来看看这个类中得迭代器。这个类中的迭代器是线程安全的,为什么会线程安全?由于在实现的next和remove方法中都加了lock了。安全性根本停不下来啊~上源代码:

 private class Itr implements Iterator<E> {
        private int remaining; // Number of elements yet to be returned
        private int nextIndex; // Index of element to be returned by next
        private E nextItem;    // Element to be returned by next call to next
        private E lastItem;    // Element returned by last call to next
        private int lastRet;   // Index of last element returned, or -1 if none         Itr() {
            final ReentrantLock lock = ArrayBlockingQueue.this.lock;   
            lock.lock();
            try {
                lastRet = -1;
                if ((remaining = count) > 0)
                    nextItem = itemAt(nextIndex = takeIndex);
            } finally {
                lock.unlock();
            }
        }         public boolean hasNext() {
            return remaining > 0;
        }         public E next() {
            final ReentrantLock lock = ArrayBlockingQueue.this.lock;
            lock.lock();   //锁定
            try {
                if (remaining <= 0)
                    throw new NoSuchElementException();
                lastRet = nextIndex;
                E x = itemAt(nextIndex);  // check for fresher value
                if (x == null) {
                    x = nextItem;         // we are forced to report old value
                    lastItem = null;      // but ensure remove fails
                }
                else
                    lastItem = x;
                while (--remaining > 0 && // skip over nulls
                       (nextItem = itemAt(nextIndex = inc(nextIndex))) == null)
                    ;
                return x;
            } finally {
                lock.unlock();
            }
        }         public void remove() {
            final ReentrantLock lock = ArrayBlockingQueue.this.lock;
            lock.lock();
            try {
                int i = lastRet;
                if (i == -1)
                    throw new IllegalStateException();
                lastRet = -1;
                E x = lastItem;
                lastItem = null;
                // only remove if item still at index
                if (x != null && x == items[i]) {
                    boolean removingHead = (i == takeIndex);
                    removeAt(i);
                    if (!removingHead)
                        nextIndex = dec(nextIndex);
                }
            } finally {
                lock.unlock();
            }
        }
    }

三,总结


对这个类的描写叙述已经代码分析完毕了,接下来我们来总结一下这个类的一些特点吧:

1.一旦创建,则容量不能再修改

2.这个类是线程安全的,而且迭代器也是线程安全的

3.这个类的put和take方法分别会在队列满了和队列空了之后被堵塞操作。

4.这个类提供了offer和poll方法来插入和提取元素,而不会在队列满了或者队列为空时堵塞操作。

5.这个队列的锁默认是不公平策略,即唤醒线程的顺序是不确定的。

java同步包种ArrayBlockingQueue类的分析与理解的更多相关文章

  1. 死磕 java集合之ArrayBlockingQueue源码分析

    问题 (1)ArrayBlockingQueue的实现方式? (2)ArrayBlockingQueue是否需要扩容? (3)ArrayBlockingQueue有什么缺点? 简介 ArrayBloc ...

  2. java多线程系列(九)---ArrayBlockingQueue源码分析

    java多线程系列(九)---ArrayBlockingQueue源码分析 目录 认识cpu.核心与线程 java多线程系列(一)之java多线程技能 java多线程系列(二)之对象变量的并发访问 j ...

  3. 并发编程(八)—— Java 并发队列 BlockingQueue 实现之 ArrayBlockingQueue 源码分析

    开篇先介绍下 BlockingQueue 这个接口的规则,后面再看其实现. 阻塞队列概要 阻塞队列与我们平常接触的普通队列(LinkedList或ArrayList等)的最大不同点,在于阻塞队列的阻塞 ...

  4. Java线程池ThreadPoolExecutor类源码分析

    前面我们在java线程池ThreadPoolExecutor类使用详解中对ThreadPoolExector线程池类的使用进行了详细阐述,这篇文章我们对其具体的源码进行一下分析和总结: 首先我们看下T ...

  5. 《手把手教你》系列技巧篇(七十一)-java+ selenium自动化测试-自定义类解决元素同步问题(详解教程)

    1.简介 前面宏哥介绍了几种关于时间等待的方法,也提到了,在实际自动化测试脚本开发过程,百分之90的报错是和元素因为时间不同步而发生报错.本文介绍如何新建一个自定义的类库来解决这个元素同步问题.这样, ...

  6. Java LinkedBlockingQueue和ArrayBlockingQueue分析

    LinkedBlockingQueue是一个链表实现的堵塞队列,在链表一头增加元素,假设队列满.就会堵塞.还有一头取出元素.假设队列为空.就会堵塞. LinkedBlockingQueue内部使用Re ...

  7. Java线程池ThreadPoolExecutor使用和分析(一)

    相关文章目录: Java线程池ThreadPoolExecutor使用和分析(一) Java线程池ThreadPoolExecutor使用和分析(二) - execute()原理 Java线程池Thr ...

  8. java并发包&线程池原理分析&锁的深度化

          java并发包&线程池原理分析&锁的深度化 并发包 同步容器类 Vector与ArrayList区别 1.ArrayList是最常用的List实现类,内部是通过数组实现的, ...

  9. MapReduce剖析笔记之八: Map输出数据的处理类MapOutputBuffer分析

    在上一节我们分析了Child子进程启动,处理Map.Reduce任务的主要过程,但对于一些细节没有分析,这一节主要对MapOutputBuffer这个关键类进行分析. MapOutputBuffer顾 ...

随机推荐

  1. MinGW安装和使用基础教程

    MinGW全称Minimalist GNU For Windows,是个精简的Windows平台C/C++.ADA及Fortran编译器,相比Cygwin而言,体积要小很多,使用较为方便.MinGW提 ...

  2. Anaconda升级

    Anaconda是可以进行升级的, 这样就省的重装一遍python全家桶了, 比如:   conda update conda conda install anaconda=2018.12   就可以 ...

  3. Word histogram

    Here is a program that reads a file and builds a histogram of the words in the file: process_file lo ...

  4. spring-data-redis 使用过程中需要注意的地方

    1.序列化问题 <!-- SDR默认采用的序列化策略有两种,一种是String的序列化策略,一种是JDK的序列化策略. StringRedisTemplate默认采用的是String的序列化策略 ...

  5. python shutil 模块 的剪切文件函数 shutil.movemove(src, dst),换用 os.rename(sourceFile, targetFile)

    Google 一搜python 剪切文件,出来shutil 这模块,网上很多人也跟疯说shutil.move(src, dst)就是用来剪切文件的,结果一试,剪切毛线,文件都复制到另一个文件夹了,源文 ...

  6. Spring学习总结(12)——Druid连接池及监控在spring配置

    Druid连接池及监控在spring配置如下: <bean id="dataSource" class="com.alibaba.druid.pool.DruidD ...

  7. CSS浏览器兼容问题集(一)

    CSS对浏览器的兼容性有时让人非常头疼,也许当你了解其中的技巧跟原理,就会认为也不是难事,从网上收集了IE7,6与Fireofx的兼容性处理方法并整理了一下.对于web2.0的过度,请尽量用xhtml ...

  8. windows開始菜单和任务栏图标显示空白而且点击时候显示项目已被移动或删除

    这几天实验室老常常自己主动断电.这是非常蛋疼的一件事,这不上次断电就出事了.来电后开机,点击任务栏上的程序全都显示为无法打开此项目,该项目已被移动.删除.原因是图标缓存丢失,可能是突然断电引起的,也有 ...

  9. 热门游戏&lt;开心消消乐&gt;的“加壳”诡计!!

    好久没搞游戏了,前几天看了又又一次看了看<开心消消乐>的1.29最新版..于是故事開始了: 1.反编译分析 首先使用Androidkiller进行反编译,得到两个Smali代码目录:mal ...

  10. android系统又一次刷ROM简记(一)

    当须要对android系统进行大刀阔斧的改造的时候,应该清晰的了解android各个image的组成才干做到庖丁解牛. 首先在android烧写过程中须要烧写的文件主要包含uboot.bin\boot ...