LinkedBlockingQueue原理

来源:互联网 发布:在淘宝上怎么分期付款 编辑:程序博客网 时间:2024/05/16 16:06



LinkedBlockingQueue继承至BlockingQueue,是一个阻塞队列。

其内部实现原理如下:

  • 通过静态内部类Node来存储元素,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; }
        }
  • 通过双锁来控制添加和删除元素,并通过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();
  • 看一下put方法
  •  public void put(E e) throws InterruptedException {
            //不可添加null元素
            if (e == null) throw new NullPointerException();
           
            int c = -1;
            final ReentrantLock putLock = this.putLock;
            final AtomicInteger count = this.count;
            putLock.lockInterruptibly();
            try {
                /**与此 Condition 相关的锁以原子方式释放,并且出于线程调度的目的,将禁用当前线程,且在发生以下四种情况之一 以前,当前线程将一直处于休眠状态: 
    *其他某个线程调用此 Condition 的 signal() 方法,并且碰巧将当前线程选为被唤醒的线程;或者 
    *其他某个线程调用此 Condition 的 signalAll() 方法;或者 
    *其他某个线程中断当前线程,且支持中断线程的挂起;或者 
    *发生“虚假唤醒” **/
                //有可能出现假唤醒,因为 Condition 应该总是在一个循环中被等待,并测试正被等待的状态声明
                while (count.get() == capacity) { 
                        notFull.await();
                }
                //入队
                enqueue(e);
                //count是AtomicInteger类型,以原子方式增长
                c = count.getAndIncrement();
                //如果队列中已添加元素的数量小于capacity,通知其它等待线程
                if (c + 1 < capacity)
                    notFull.signal();
            } finally {
            //在finally块中释放锁,保证锁一定释放掉
                putLock.unlock();
            }
            //如果队列中一个元素时,通知在读取锁上阻塞的线程
            if (c == 0)
                signalNotEmpty();
        }
        //入队,last总是指向最后一个元素
         private void enqueue(E x) {
            // assert putLock.isHeldByCurrentThread();
            last = last.next = new Node<E>(x);
        }
        
        private void signalNotEmpty() {
            final ReentrantLock takeLock = this.takeLock;
            takeLock.lock();
            try {
                notEmpty.signal();
            } finally {
                takeLock.unlock();
            }
        }
  • take方法解析
  •     public E take() throws InterruptedException {
            E x;
            int c = -1;
            final AtomicInteger count = this.count;
            final ReentrantLock takeLock = this.takeLock;
            takeLock.lockInterruptibly();
            try {
                    //若队列为空,此线程释放读取锁,且线程阻塞,与put方法类似
                    while (count.get() == 0) {
                        notEmpty.await();
                    }
                x = dequeue();
                c = count.getAndDecrement();
                //如果队列中还有元素,唤醒下个此condition阻塞的线程
                if (c > 1)
                    notEmpty.signal();
            } finally {
                takeLock.unlock();
            }
            //如查当前队列中元素数量与capacity相等,去唤醒在写锁上阻塞的线程
            //个人感觉应该是当前队列中元素数量小于capacity时,再去唤醒写锁上阻塞的线程吧?
            if (c == capacity)
                signalNotFull();
            return x;
        }
        
           private E dequeue() {
            Node<E> h = head;
            Node<E> first = h.next;
            h.next = h; // help GC
            head = first;
            E x = first.item;
            first.item = null;
            return x;
        }
  • Head元素中的E始终为null,当移移除元素时,将当前head指向的结点移除,而返回的值是head->next结点中的E,也就是El1中的E,并且将El1中的E设置为空,且head指向它


原创粉丝点击