Java集合(六)ArrayBlockingQueue、LinkedBlockingQueue使用及源码分析

  本系列文章:
    Java集合(一)集合框架概述
    Java集合(二)ArrayList、LinkedList使用及源码分析
    Java集合(三)Vector、Stack使用及源码分析
    Java集合(四)HashMap、Hashtable、LinkedHashMap使用及源码分析
    Java集合(五)HashSet、LinkedHashSet使用及源码分析
    Java集合(六)ArrayBlockingQueue、LinkedBlockingQueue使用及源码分析

ArrayBlockingQueue

一、ArrayBlockingQueue概述

  • 1、ArrayBlockingQueue的继承关系
public class ArrayBlockingQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable

  从继承关系看:

  1. 继承AbstractQueue,实现BlockingQueue接口,具备队列的相关操作。
  2. 实现Serializable接口,可以序列化。
  • 2、ArrayBlockingQueue的底层结构
      BlockingQueue是一个阻塞队列(队列,是一种线性表,它的特点是先进先出,即FIFO)。ArrayBlockingQueue是一个由数组支持的有界阻塞队列。它的本质是一个基于数组的BlockingQueue的实现。
      ArrayBlockingQueue结构图示:

二、ArrayBlockingQueue特点

  • 1、ArrayBlockingQueue是一个由数组支持的有界队列,此队列按FIFO(先进先出)原则对元素进行排序。
  • 2、新元素插入到队列的尾部,队列获取操作则是从队列头部开始获得元素。
  • 3、ArrayBlockingQueue是一个简单的“有界缓存区”,一旦创建,就不能在增加其容量。
  • 4、在向已满的队列中添加元素会导致操作阻塞,从空队列中提取元素也将导致阻塞。
  • 5、此类支持对等待的生产者线程和使用者线程进行排序的可选公平策略。默认情况下,不保证是这种排序的(即采用非公平策略)。可以将公平性(fair)设置为true,而构造的队列允许按照FIFO顺序访问线程。公平性通常会降低吞吐量,但可以保证线程的“先来后到”。

三、ArrayBlockingQueue使用

3.1 常用方法介绍

  • 1、创建具有指定容量和默认锁策略的 ArrayBlockingQueue
	public ArrayBlockingQueue(int capacity)
  • 2、创建一个具有指定容量和指定锁策略的 ArrayBlockingQueue
	public ArrayBlockingQueue(int capacity, boolean fair)
  • 3、添加元素到队列的尾部
	public boolean add(E e)
  • 4、删除所有的元素
	public void clear()
  • 5、如果此队列包含指定的元素,则返回 true
	public boolean contains(Object o)
  • 6、获取迭代器
	public Iterator<E> iterator()
  • 7、添加元素到队列尾部
	public boolean offer(E e)
  • 8、在该队列的尾部插入指定的元素,等待指定的等待时间,以使空间在队列已满时变为可用
	public boolean offer(E e, long timeout, TimeUnit unit)
  • 9、检索但不删除此队列的头元素,如果此队列为空,则返回 null
	public E peek()
  • 10、检索并删除此队列的头元素,如果此队列为空,则返回 null
	public E poll()
  • 11、检索并删除此队列的头元素,等待指定的等待时间(如有必要)使元素变为可用
	public E poll(long timeout, TimeUnit unit) throws InterruptedException
  • 12、在该队列的尾部插入指定的元素
	public void put(E e) throws InterruptedException
  • 13、从该队列中删除指定元素的单个实例(如果存在)
	public boolean remove(Object o)
  • 14、返回此队列中的元素数
	public int size()
  • 15、检索并删除此队列的头元素,如有必要,等待元素可用
	public E take() throws InterruptedException

3.2 常用方法使用

        //1.构造方法
		ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(10);
		ArrayBlockingQueue<String> arrayBlockingQueue2 = new ArrayBlockingQueue<>(10,false);
		
		//2.添加元素到队列尾部
		arrayBlockingQueue.add("aaa");
		System.out.println(arrayBlockingQueue); //[aaa]
		
		//3.删除所有的元素
		arrayBlockingQueue.clear();
		System.out.println(arrayBlockingQueue); //[]
		
		//4.判断是否包含指定的元素
		arrayBlockingQueue.add("aaa");
		arrayBlockingQueue.add("bbb");
		arrayBlockingQueue.add("ccc");
		System.out.println(arrayBlockingQueue.contains("aaa")); //true
		
		//5.获取迭代器
		arrayBlockingQueue.add("aaa");
		arrayBlockingQueue.add("bbb");
		arrayBlockingQueue.add("ccc");
		Iterator<String> iterator = arrayBlockingQueue.iterator();
		while(iterator.hasNext()) {
    
    
			System.out.print(iterator.next()+" "); //aaa bbb ccc aaa bbb ccc 
		}
		System.out.println();
		
		//6.添加元素到队列尾部
		arrayBlockingQueue.offer("ddd");
		System.out.println(arrayBlockingQueue); //[aaa, bbb, ccc, aaa, bbb, ccc, ddd]
		
		//7.在该队列的尾部插入指定的元素,等待指定的等待时间,以使空间在队列已满时变为可用
		try {
    
    
			arrayBlockingQueue.offer("eee",2000,TimeUnit.MILLISECONDS);
		} catch (InterruptedException e) {
    
    
			e.printStackTrace();
		}
		System.out.println(arrayBlockingQueue); //[aaa, bbb, ccc, aaa, bbb, ccc, ddd, eee]
		
		//8.检索但不删除此队列的头元素
		System.out.println(arrayBlockingQueue.peek()); //aaa
		System.out.println(arrayBlockingQueue); //[aaa, bbb, ccc, aaa, bbb, ccc, ddd, eee]
		
		//9.检索并删除此队列的头元素
		System.out.println(arrayBlockingQueue.poll()); //aaa
		System.out.println(arrayBlockingQueue); //[bbb, ccc, aaa, bbb, ccc, ddd, eee]
		
		//10.检索并删除此队列的头元素,等待指定的等待时间(如有必要)使元素变为可用
		try {
    
    
			System.out.println(arrayBlockingQueue.poll(2000,TimeUnit.MILLISECONDS)); //bbb
		} catch (InterruptedException e) {
    
    
			e.printStackTrace();
		} 
		System.out.println(arrayBlockingQueue); //[ccc, aaa, bbb, ccc, ddd, eee]
		
		//11.在该队列的尾部插入指定的元素
		try {
    
    
			arrayBlockingQueue.put("fff");
		} catch (InterruptedException e) {
    
    
			e.printStackTrace();
		}
		System.out.println(arrayBlockingQueue); //[ccc, aaa, bbb, ccc, ddd, eee, fff]
		
		//12.删除元素
		arrayBlockingQueue.remove("ddd");
		System.out.println(arrayBlockingQueue); //[ccc, aaa, bbb, ccc, eee, fff]
		
		//13.返回此队列中的元素数
		System.out.println(arrayBlockingQueue.size()); //6
		
		//14.检索并删除此队列的头元素
		try {
    
    
			System.out.println(arrayBlockingQueue.take()); //ccc
		} catch (InterruptedException e) {
    
    
			e.printStackTrace();
		}
		System.out.println(arrayBlockingQueue); //[aaa, bbb, ccc, eee, fff]

四、ArrayBlockingQueue源码分析

  先看一些变量:

    private static final long serialVersionUID = -817911632652898426L;
	//存储对象的数组
    final Object[] items;
    //用来为下一个take/poll/remove的索引(出队)
    int takeIndex;	
    //用来为下一个put/offer/add的索引(入队)
    int putIndex;
	//队列中元素的个数
    int count;
	//重入锁,出队和入队持有这一把锁
    final ReentrantLock lock;	
    //出队的条件
    private final Condition notEmpty;
	//入队的条件
    private final Condition notFull;
    //Itrs 维护一个 Itr 链表,用于在一个队列下的多个 Itr 迭代器中共享队列元素,
    //保证多个迭代器中的元素数据的一致性
    transient Itrs itrs = null;

4.1 构造方法

  • 1、ArrayBlockingQueue(int capacity, boolean fair)
	//构造一个指定初始容量和指定公平锁策略的ArrayBlockingQueue
    public ArrayBlockingQueue(int capacity, boolean fair) {
    
    
        if (capacity <= 0)
            throw new IllegalArgumentException();
        // 初始化底层数组
        this.items = new Object[capacity];
        //若fair为false,则为非公平锁;若fair为true,则为公平锁
        lock = new ReentrantLock(fair);
        //阻塞出队条件
        notEmpty = lock.newCondition();
        //阻塞入队条件
        notFull =  lock.newCondition();
    }
  • 2、ArrayBlockingQueue(int capacity)
	//指定初始容量,采用非公平锁策略
	public ArrayBlockingQueue(int capacity) {
    
    
       this(capacity, false);
    }
  • 3、public ArrayBlockingQueue(int capacity, boolean fair, Collection<? extends E> c)
	//指定初始容量、公平锁策略和指定集合
    public ArrayBlockingQueue(int capacity, boolean fair,
                              Collection<? extends E> c) {
    
    
        //先构造一个ArrayBlockingQueue
        this(capacity, fair);
		
        final ReentrantLock lock = this.lock;
        //加锁
        lock.lock(); 
        try {
    
    
            int i = 0;
            try {
    
    
            	//遍历集合中的元素,将这些元素放置在队列的底层数组中
                for (E e : c) {
    
    
                	//检测元素是否为空,若为空则抛出NullPointerException
                    checkNotNull(e);
                    items[i++] = e;
                }
            } catch (ArrayIndexOutOfBoundsException ex) {
    
    
                throw new IllegalArgumentException();
            }
            //更新队列中的元素数
            count = i;
            //设置putIndex的位置,如果数组已满的话设置为0
            //更新putIndex后,再有元素入队时,便从该索引位置开始入队
            putIndex = (i == capacity) ? 0 : i;
        } finally {
    
    
        	//释放锁
            lock.unlock();
        }
    }

    private static void checkNotNull(Object v) {
    
    
        if (v == null)
            throw new NullPointerException();
    }

4.2 添加元素

  • 1、offer(E e)
    public boolean offer(E e) {
    
    
    	//检测是否为空,若为空则抛出NullPointerException
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        //加锁
        lock.lock();
        try {
    
    
        	//若队列已满,不能再添加
            if (count == items.length)
                return false;
            //队列未满时,就让元素入队
            else {
    
    
                enqueue(e);
                return true;
            }
        } finally {
    
    
        	//释放锁
            lock.unlock();
        }
    }

  元素入队用的具体方法是enqueue(E x):

    private void enqueue(E x) {
    
    
        final Object[] items = this.items;
        items[putIndex] = x;
        //当putIndex 等于数组长度(队列中元素已满)时,将 putIndex 重置为 0
        if (++putIndex == items.length)
            putIndex = 0;
        //队列元素个数+1
        count++;
        //唤醒处于等待状态下的线程,表示当前队列中的元素不为空,如果存在
        //消费者线程阻塞,就可以开始取出元素
        notEmpty.signal();
    }

  putIndex 为什么会在等于数组长度的时候重新设置为 0?因为 ArrayBlockingQueue 是一个 FIFO 的队列,队列添加元素时,是从队尾获取 putIndex 来存储元素,当 putIndex等于数组长度时,下次就需要从数组头部开始添加了。

  • 2、offer(E e, long timeout, TimeUnit unit)
      这个方法的作用和上一个方法的作用相似,不过带有超时等待功能。
    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);
            }
            //元素入队
            enqueue(e);
            return true;
        } finally {
    
    
        	//释放锁
            lock.unlock();
        }
    }
  • 3、put(E e)
	//put 方法和 add 方法功能一样(添加元素),差异是 put 方法如果队列满了,会阻塞
    public void put(E e) throws InterruptedException {
    
    
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
    
    
            while (count == items.length)
            	//队列满了的情况下,当前线程将会被 notFull 条件对象挂起加到等待队列中
                notFull.await();
            //元素入队
            enqueue(e);
        } finally {
    
    
            lock.unlock();
        }
    }

4.3 删除元素

  • 1、remove(Object o)
    public boolean remove(Object o) {
    
    
    	//如果o为null,不删除任何元素
        if (o == null) return false;
        final Object[] items = this.items;
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
    
    
        	//如果队列中有元素
            if (count > 0) {
    
    
            	//获取下一个要添加元素时的索引
                final int putIndex = this.putIndex;
                //获取当前要被移除的元素的索引
                int i = takeIndex;
                do {
    
    
                	//从takeIndex 下标开始,寻找要被删除的元素
                    if (o.equals(items[i])) {
    
    
                   	 	//根据下标,删除元素
                        removeAt(i);
                        return true;
                    }
                    //当前删除索引执行加 1 后判断是否与数组长度相等
    				//若为 true,说明索引已到数组尽头,将 i 设置为 0
                    if (++i == items.length)
                        i = 0;
                //继续查找,直到找到最后一个元素
                } while (i != putIndex);
            }
            return false;
        } finally {
    
    
            lock.unlock();
        }
    }
	//根据下标删除元素
    void removeAt(final int removeIndex) {
    
    
        final Object[] items = this.items;
        //要删除的元素在takeIndex位置
        if (removeIndex == takeIndex) {
    
    
            //将takeIndex位置的元素置为null
            items[takeIndex] = null;
            //类似于putIndex,当takeIndex到队列尾部时,从头开始
            if (++takeIndex == items.length)
                takeIndex = 0;
            count--;
            if (itrs != null)
                itrs.elementDequeued();
        //要删除的元素不在takeIndex位置
        } else {
    
    
            final int putIndex = this.putIndex;
            for (int i = removeIndex;;) {
    
    
                int next = i + 1;
                if (next == items.length)
                    next = 0;
                if (next != putIndex) {
    
    
                    items[i] = items[next];
                    i = next;
                } else {
    
    
                    items[i] = null;
                    this.putIndex = i;
                    break;
                }
            }
            count--;
            if (itrs != null)
                itrs.removedAt(removeIndex);
        }
        //触发 因为队列满了以后导致的被阻塞的线程
        notFull.signal();
    }
  • 2、poll()
	//当队列中存在元素,则从队列中取出一个元素,如果队列为空,则直接返回 null
    public E poll() {
    
    
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
    
    
        	//当队列中有元素时,元素出队
            return (count == 0) ? null : dequeue();
        } finally {
    
    
            lock.unlock();
        }
    }
  • 3、poll(long timeout, TimeUnit unit)
	//带超时机制的获取数据,如果队列为空,则会等待指定的时间再去获取元素返回
    public E poll(long timeout, TimeUnit unit) throws InterruptedException {
    
    
        long nanos = unit.toNanos(timeout);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
    
    
        	//当队列中没有元素时,进行超时时间等待
            while (count == 0) {
    
    
                if (nanos <= 0)
                    return null;
                //更新等待超时时间
                nanos = notEmpty.awaitNanos(nanos);
            }
            return dequeue();
        } finally {
    
    
            lock.unlock();
        }
    }
	//元素出队
    private E dequeue() {
    
    
        final Object[] items = this.items;
        @SuppressWarnings("unchecked")
        E x = (E) items[takeIndex];
        //出队位置的元素置为null
        items[takeIndex] = null;
        //如果takeIndex到了末尾,从队列头部开始
        if (++takeIndex == items.length)
            takeIndex = 0;
        //元素个数-1
        count--;
        if (itrs != null)
            itrs.elementDequeued();
        //唤醒因队列满而被阻塞的线程
        notFull.signal();
        return x;
    }
  • 4、take()
	//基于阻塞的方式获取队列中的元素,如果队列为空,则 take 方法会一直阻塞,
	//直到队列中有新的数据可以消费
    public E take() throws InterruptedException {
    
    
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
    
    
        	//当队列中没有元素时,就一直等待
            while (count == 0)
                notEmpty.await();
            return dequeue();
        } finally {
    
    
            lock.unlock();
        }
    }

4.4 查找元素

  • 1、peek()
	//获取头元素如果没有获取到则返回null。仅仅是获取不移除
    public E peek() {
    
    
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
    
    
        	//根据takeIndex,获取队首元素
            return itemAt(takeIndex);
        } finally {
    
    
            lock.unlock();
        }
    }
  • 2、contains(Object o)
	//返回队列是否包含当前元素
    public boolean contains(Object o) {
    
    
        if (o == null) return false;
        final Object[] items = this.items;
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
    
    
        	//队列不为空
            if (count > 0) {
    
    
                final int putIndex = this.putIndex;
                int i = takeIndex;
                do {
    
    
                	//从takeIndex位置开始遍历
                    if (o.equals(items[i]))
                        return true;
                    //遍历到数组末尾时,从0位置继续遍历
                    if (++i == items.length)
                        i = 0;
                //当takeIndex==putIndex,代表队列已经遍历结束
                } while (i != putIndex);
            }
            return false;
        } finally {
    
    
            lock.unlock();
        }
    }

4.5 清空队列

    public void clear() {
    
    
        final Object[] items = this.items;
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
    
    
            int k = count;
            //队列不为空
            if (k > 0) {
    
    
                final int putIndex = this.putIndex;
                int i = takeIndex;
                do {
    
    
                	//将数组元素逐个值为null
                    items[i] = null;
                    if (++i == items.length)
                        i = 0;
                } while (i != putIndex);
                //数组中元素清空时,入队索引和出队索引处在了同一个位置上
                takeIndex = putIndex;
                count = 0;
                if (itrs != null)
                    itrs.queueIsEmpty();
                for (; k > 0 && lock.hasWaiters(notFull); k--)
                	//检测当前是否有线程已调用condition.await()并且处于await状态
                    notFull.signal();
            }
        } finally {
    
    
            lock.unlock();
        }
    }

4.6 获取队列元素个数

    public int size() {
    
    
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
    
    
            return count;
        } finally {
    
    
            lock.unlock();
        }
    }

LinkedBlockingQueue

一、LinkedBlockingQueue概述

  • 1、LinkedBlockingQueue的继承关系
public class LinkedBlockingQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable

  从继承关系看,LinkedBlockingQueue的特点:

  1. 继承AbstractQueue,实现BlockingQueue接口,具备队列的相关操作。
  2. 实现Serializable接口,可以序列化。
  • 2、 LinkedBlockingQueue的底层结构
      LinkedBlockingQueue是一个基于链表实现的可选容量的阻塞队列,内部是由节点Node构成。在LinkedBlockingQueue中,队列的头节点head是不存元素的,它的item是null,next指向的元素才是真正的第一个元素,它也有两个用于阻塞等待的Condition条件对象。
      LinkedBlockingQueue的结构:

二、 LinkedBlockingQueue特点

  • 1、LinkedBlockingQueue特点
  1. 基于链表实现的队列,从头部获取元素,在尾部插入元素,比基于数组的队列吞吐量更高。
  2. 双锁队列的变种实现,一把写锁,一把读锁。
  3. 默认队列的大小是Integer的最大值,如果添加速度大于读取速度的话,有可能造成内存溢出。
  4. 因为是两把锁,所以元素的个数使用了一个原子类型的变量来维护(AtomicInteger)。
  • 2、LinkedBlockingQueue为什么要使用两把锁,ArrayBlockingQueue使用一把锁,不是也可以保证线程的安全么?
      使用两把锁,可以保证元素的插入和删除并不互斥,从而能够同时进行,达到提高吞吐量的的效果。

三、LinkedBlockingQueue使用

3.1 常用方法介绍

  • 1、创建一个 LinkedBlockingQueue ,容量为Integer.MAX_VALUE
	public LinkedBlockingQueue()
  • 2、创建一个具有给定(固定)容量的 LinkedBlockingQueue
	public LinkedBlockingQueue(int capacity)
  • 3、从这个队列中原子地删除所有的元素
	public void clear()
  • 4、如果此队列包含指定的元素,则返回 true
	public boolean contains(Object o)
  • 5、返回该队列中的元素的迭代器
	public Iterator<E> iterator()
  • 6、如果可以在不超过队列的容量的情况下立即将其指定的元素插入到队列的尾部,如果队列已满,则不能插入元素,返回false
	public boolean offer(E e)
  • 7、在该队列的尾部插入指定的元素,必要时等待指定的等待时间才能使空间变得可用
	public boolean offer(E e, long timeout, TimeUnit unit)
  • 8、检索但不删除此队列的头元素,如果此队列为空,则返回 null
	public E peek()
  • 9、检索并删除此队列的头元素,如果此队列为空,则返回 null
	public E poll()
  • 10、检索并删除此队列的头元素,等待指定的等待时间(如有必要)使元素变为可用
	public E poll(long timeout, TimeUnit unit)
  • 11、在该队列的尾部插入指定的元素,如果需要,等待队列变为可用
	public void put(E e)
  • 12、从该队列中删除指定元素的单个实例(如果存在)
	public boolean remove(Object o)
  • 13、返回此队列中的元素数
	public int size()
  • 14、检索并删除此队列的头元素,如有必要,等待元素可用
	public E take()

3.2 常见方法使用

        //1.构造方法
		LinkedBlockingQueue<String> linkedBlockingQueue = new LinkedBlockingQueue<>();
		LinkedBlockingQueue<String> linkedBlockingQueue3 = new LinkedBlockingQueue<>(30);
		
		//2.删除所有的元素
		linkedBlockingQueue.add("aaa");
		linkedBlockingQueue.clear();
		System.out.println(linkedBlockingQueue); //[]
		
		//3.判断是否包含指定的元素
		linkedBlockingQueue.add("aaa");
		linkedBlockingQueue.add("bbb");
		linkedBlockingQueue.add("ccc");
		System.out.println(linkedBlockingQueue.contains("aaa")); //true
		
		//4.获取迭代器
		Iterator<String> iterator = linkedBlockingQueue.iterator();
		while(iterator.hasNext()) {
    
    
			System.out.print(iterator.next()+" "); //aaa bbb ccc
		}
		System.out.println();
		
		//5.添加元素到队列尾部
		linkedBlockingQueue.offer("eee");
		System.out.println(linkedBlockingQueue); //[aaa, bbb, ccc, eee]
		
		//6.该队列的尾部插入指定的元素,等待指定的等待时间,以使空间在队列已满时变为可用
		try {
    
    
			linkedBlockingQueue.offer("fff",2000,TimeUnit.MILLISECONDS);
		} catch (InterruptedException e) {
    
    
			e.printStackTrace();
		}
		System.out.println(linkedBlockingQueue);  //[aaa, bbb, ccc, eee, fff]
		
		//7.检索但不删除此队列的头元素
		System.out.println(linkedBlockingQueue.peek()); //aaa
		System.out.println(linkedBlockingQueue); //[aaa, bbb, ccc, eee, fff]
		
		//8.检索并删除此队列的头元素
		System.out.println(linkedBlockingQueue.poll()); //aaa
		System.out.println(linkedBlockingQueue); //[bbb, ccc, eee, fff]
		
		//9.检索并删除此队列的头元素,等待指定的等待时间(如有必要)使元素变为可用
		try {
    
    
			System.out.println(linkedBlockingQueue.poll(2000,TimeUnit.MILLISECONDS)); //bbb
		} catch (InterruptedException e) {
    
    
			e.printStackTrace();
		} 
		System.out.println(linkedBlockingQueue); //[ccc, eee, fff]
		
		//10.在该队列的尾部插入指定的元素
		try {
    
    
			linkedBlockingQueue.put("ggg");
		} catch (InterruptedException e) {
    
    
			e.printStackTrace();
		}
		System.out.println(linkedBlockingQueue); //[ccc, eee, fff, ggg]
		
		//11.删除元素
		System.out.println(linkedBlockingQueue.remove("ddd")); //false
		System.out.println(linkedBlockingQueue); //[ccc, eee, fff, ggg]
		
		//12.返回此队列中的元素数
		System.out.println(linkedBlockingQueue.size()); //4
		
		//13.检索并删除此队列的头元素
		try {
    
    
			System.out.println(linkedBlockingQueue.take()); //ccc
		} catch (InterruptedException e) {
    
    
			e.printStackTrace();
		}
		System.out.println(linkedBlockingQueue); //[eee, fff, ggg]

四、LinkedBlockingQueue源码

4.1 节点

  先看LinkedBlockingQueue中所存储的节点的定义:

    static class Node<E> {
    
    
    	//当前节点value
        E item;
        //下一个节点
        Node<E> next;
        Node(E x) {
    
     item = x; }
    }

  从这个节点定义可以看出,LinkedBlockingQueue是一个单向链表构成的队列
  接着看一些LinkedBlockingQueue中的变量:

    //队列容量
    private final int capacity;
    //当前队列元素数量
    private final AtomicInteger count = new AtomicInteger();
	//头结点,不存数据
    transient Node<E> head;
	//尾节点
    private transient Node<E> last;
    //出队锁,只有take,poll方法会持有
    private final ReentrantLock takeLock = new ReentrantLock();
    //出队等待条件
    private final Condition notEmpty = takeLock.newCondition();
    //入队锁,只有put,offer会持有
    private final ReentrantLock putLock = new ReentrantLock();
    //入队等待条件
    private final Condition notFull = putLock.newCondition();

4.2 构造方法

  • 1、LinkedBlockingQueue()
    public LinkedBlockingQueue() {
    
    
    	//未指定队列容量时,容量为Integer.MAX_VALUE
        this(Integer.MAX_VALUE);
    }
  • 2、LinkedBlockingQueue(int capacity)
	//指定初始容量的队列
    public LinkedBlockingQueue(int capacity) {
    
    
        if (capacity <= 0) throw new IllegalArgumentException();
        this.capacity = capacity;
        //初始化首尾节点
        last = head = new Node<E>(null);
    }
  • 3、LinkedBlockingQueue(Collection<? extends E> c)
    public LinkedBlockingQueue(Collection<? extends E> c) {
    
    
    	//初始化容量
        this(Integer.MAX_VALUE);
        //初始化入队锁
        final ReentrantLock putLock = this.putLock;
        //加锁
        putLock.lock(); 
        try {
    
    
            int n = 0;
            //遍历集合
            for (E e : c) {
    
    
                if (e == null)
                    throw new NullPointerException();
                if (n == capacity)
                    throw new IllegalStateException("Queue full");
                //入队
                enqueue(new Node<E>(e));
                ++n;
            }
            //更新队列中元素数量
            count.set(n);
        } finally {
    
    
        	//释放锁
            putLock.unlock();
        }
    }

4.3 添加元素

  • 1、offer(E e)
	//元素入队
    public boolean offer(E e) {
    
    
    	//不能添加非null元素
        if (e == null) throw new NullPointerException();
        final AtomicInteger count = this.count;
        //如果队列已满,不能再往队列中添加元素
        if (count.get() == capacity)
            return false;
        int c = -1;
        //构建新节点
        Node<E> node = new Node<E>(e);
        //获取入队锁putLock
        final ReentrantLock putLock = this.putLock;
        //加锁
        putLock.lock();
        try {
    
    
        	//如果队列未满,元素入队
            if (count.get() < capacity) {
    
    
                enqueue(node);
                //更新队列中元素数量
                c = count.getAndIncrement();
                //如果队列未满
                if (c + 1 < capacity)
                	//唤醒一个入队条件队列中阻塞的线程
                    notFull.signal();
            }
        } finally {
    
    
        	//释放锁
            putLock.unlock();
        }
        //节点数量为0,说明队列是空的
        if (c == 0)
        	//唤醒一个出队条件队列阻塞的线程
            signalNotEmpty();
        return c >= 0;
    }
	//唤醒一个出队条件队列阻塞的线程
    private void signalNotEmpty() {
    
    
    	//获取出队锁
        final ReentrantLock takeLock = this.takeLock;
        takeLock.lock();
        try {
    
    
            notEmpty.signal();
        } finally {
    
    
            takeLock.unlock();
        }
    }

  元素入队的具体方法是:enqueue(node),实现:

扫描二维码关注公众号,回复: 13415686 查看本文章
    private void enqueue(Node<E> node) {
    
    
    	//将当前尾节点的next指针指向新节点,再把last指向新节点
        last = last.next = node;
    }

  图示:

  • 2、offer(E e, long timeout, TimeUnit unit)
	//在队尾插入一个元素,超时则不尝试,支持中断。当前节点入队后,如果
	//队列没满,就唤醒一个入队的线程让其入队
    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) {
    
    
            	//如果超时,返回false
                if (nanos <= 0)
                    return false;
                //更新时间
                nanos = notFull.awaitNanos(nanos);
            }
            //元素入队
            enqueue(new Node<E>(e));
            //更新队列中元素数量
            c = count.getAndIncrement();
            //说明当前元素后面还能再插入一个
            //就唤醒一个入队条件队列中阻塞的线程
            if (c + 1 < capacity)
                notFull.signal();
        } finally {
    
    
        	//释放锁
            putLock.unlock();
        }
        //节点数量为0,说明队列是空的
        if (c == 0)
        	//唤醒一个出队条件队列阻塞的线程
            signalNotEmpty();
        return true;
    }
  • 3、put(E e)
    public void put(E e) throws InterruptedException {
    
    
        if (e == null) throw new NullPointerException();
        int c = -1;
        //创建节点
        Node<E> node = new Node<E>(e);
        //获取入队锁,支持中断
        final ReentrantLock putLock = this.putLock;
        //获取队列中元素数量
        final AtomicInteger count = this.count;
        putLock.lockInterruptibly();
        try {
    
    
        	//队列满了,进入阻塞状态
            while (count.get() == capacity) {
    
    
                notFull.await();
            }
            //元素入队
            enqueue(node);
            c = count.getAndIncrement();
            //如果还可以插入元素,那么释放等待的入队线程
            if (c + 1 < capacity)
                notFull.signal();
        } finally {
    
    
            putLock.unlock();
        }
        //通知出队线程队列非空
        if (c == 0)
            signalNotEmpty();
    }

4.4 删除元素

  • 1、poll()
	//检索并删除此队列的头元素
    public E poll() {
    
    
        final AtomicInteger count = this.count;
        //队列未空,返回null
        if (count.get() == 0)
            return null;
        E x = null;
        int c = -1;
        //获取独占锁
        final ReentrantLock takeLock = this.takeLock;
        takeLock.lock();
        try {
    
    
        	//队列不空则出队
            if (count.get() > 0) {
    
    
                x = dequeue();
                //队列中数量-1
                c = count.getAndDecrement();
                if (c > 1)
                    notEmpty.signal();
            }
        } finally {
    
    
            takeLock.unlock();
        }
        //如果出队前,队列是满的,则唤醒一个被take()阻塞的线程
        if (c == capacity)
            signalNotFull();
        return x;
    }

    private void signalNotFull() {
    
    
        final ReentrantLock putLock = this.putLock;
        putLock.lock();
        try {
    
    
            notFull.signal();
        } finally {
    
    
            putLock.unlock();
        }
    }
  • 2、poll(long timeout, TimeUnit unit)
	//检索并删除此队列的头元素,可以超时等待
    public E poll(long timeout, TimeUnit unit) throws InterruptedException {
    
    
        E x = null;
        int c = -1;
        //转化为纳秒
        long nanos = unit.toNanos(timeout);
        final AtomicInteger count = this.count;
        //获取独占锁
        final ReentrantLock takeLock = this.takeLock;
        takeLock.lockInterruptibly();
        try {
    
    
            while (count.get() == 0) {
    
    
            	//超时时间到,返回null
                if (nanos <= 0)
                    return null;
                nanos = notEmpty.awaitNanos(nanos);
            }
            //元素出队
            x = dequeue();
            c = count.getAndDecrement();
            //如果队列中还有数据可取,释放notEmpty条件等待队列中的第一个线程
            if (c > 1)
                notEmpty.signal();
        } finally {
    
    
            takeLock.unlock();
        }
        //如果出队前,队列是满的,则唤醒一个被take()阻塞的线程
        if (c == capacity)
            signalNotFull();
        return x;
    }
  • 3、take()
	//检索并删除此队列的头元素
    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();
            }
            //队列不为空,元素出队
            x = dequeue();
            //队列中元素个数-1
            c = count.getAndDecrement();
            //如果队列中还有数据可取,释放notEmpty条件等待队列中的第一个线程
            if (c > 1)
                notEmpty.signal();
        } finally {
    
    
            takeLock.unlock();
        }
        //如果出队前,队列是满的,则唤醒一个被take()阻塞的线程
        if (c == capacity)
            signalNotFull();
        return x;
    }
  • 4、remove(Object o)
 public boolean remove(Object o) {
    
    
        //因为队列不包含null元素,返回false
        if (o == null) return false;
        //获取两把锁
        fullyLock();
        try {
    
    
            //从头的下一个节点开始遍历
            for (Node<E> trail = head, p = trail.next;
                 p != null;
                 trail = p, p = p.next) {
    
    
                 //如果匹配,那么将节点从队列中移除,trail表示前驱节点
                if (o.equals(p.item)) {
    
    
                    unlink(p, trail);
                    return true;
                }
            }
            return false;
        } finally {
    
    
            //释放两把锁
            fullyUnlock();
        }
    }

4.5 查找元素

  • 1、peek()
	//检索但不删除此队列的头元素
    public E peek() {
    
    
    	//队列未空,返回null
        if (count.get() == 0)
            return null;
        final ReentrantLock takeLock = this.takeLock;
        takeLock.lock();
        try {
    
    
        	//因为head实际上是个空节点,所以要返回的是head的下一个节点
            Node<E> first = head.next;
            if (first == null)
                return null;
            else
                return first.item;
        } finally {
    
    
            takeLock.unlock();
        }
    }
  • 2、contains(Object o)
    public boolean contains(Object o) {
    
    
        if (o == null) return false;
        //加两把锁
        fullyLock();
        try {
    
    
        	//从头结点从前向后遍历
            for (Node<E> p = head.next; p != null; p = p.next)
                if (o.equals(p.item))
                    return true;
            return false;
        } finally {
    
    
        	//释放两把锁
            fullyUnlock();
        }
    }

4.6 清空队列

  • clear()
    public void clear() {
    
    
    	//加两把锁
        fullyLock();
        try {
    
    
        	//从头结点开始,全部元素置为null
            for (Node<E> p, h = head; (p = h.next) != null; h = p) {
    
    
                h.next = h;
                p.item = null;
            }
            //头尾节点置为同一个节点
            head = last;
 
            if (count.getAndSet(0) == capacity)
                notFull.signal();
        } finally {
    
    
        	//释放两把锁
            fullyUnlock();
        }
    }

4.7 获取队列元素个数

  • size()
    public int size() {
    
    
        return count.get();
    }

猜你喜欢

转载自blog.csdn.net/m0_37741420/article/details/120335698