并发编程之LinkedBlockingQueue源码解析

public class LinkedBlockingQueue<E>
extends AbstractQueue<E>
implements BlockingQueue<E>, Serializable
/**
*基于链表节点的可选有界阻塞队列。此队列对元素FIFO(先进先出)进行排序。
*队列的头是队列中出现时间最长的元素。队列的尾部是在队列中出现的时间最短的元素。
*在队列的尾部插入新元素,队列检索操作在队列的头部获取元素。
*与基于阵列的队列相比,链接队列通常具有更高的吞吐量,但在大多数并发应用程序中,其性能不太可预测。
*
*如果未指定,则容量等于Integer.MAX_值。每次插入时都会动态创建链接节点,除非这会使队列超过容量。
*因此建议指定容量以免内存泄漏
*
*这个类及其迭代器实现了集合和迭代器接口的所有可选方法。
*这个类是Java集合框架的成员。
* @since 1.5
* @author Doug Lea
**/
public class LinkedBlockingQueue<E>
extends AbstractQueue<E>
implements BlockingQueue<E>, Serializable

属性分析

 /**
     * 链表节点类
     */
    static class Node<E> {
        E item;

        /**
         * One of:
         * - 下一个后继节点,是
         * - 如果为null表示此节点为最后一个节点
         */
        Node<E> next;

        Node(E x) { item = x; }
    }

  /** 队列边界,如果不指定大小为Integer.MAX_VALUE */
    private final int capacity;

 /** 队列元素数量 */
    private final AtomicInteger count = new AtomicInteger();

 /**
     * 队列头节点
     * Invariant: head.item == null
     */
    transient Node<E> head;

/**
     * 队列尾节点
     * Invariant: last.next == null
     */
    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();


每个添加到LinkedBlockingQueue队列中的数据都将被封装成Node节点,添加的链表队列中,其中head和last分别指向队列的头结点和尾结点。与ArrayBlockingQueue不同的是,LinkedBlockingQueue内部分别使用了takeLock 和 putLock 对并发进行控制,也就是说,添加和删除操作并不是互斥操作,可以同时进行,这样也就可以大大提高吞吐量。

这里如果不指定队列的容量大小,也就是使用默认的Integer.MAX_VALUE,如果存在添加速度大于删除速度时候,有可能会内存溢出
另外,LinkedBlockingQueue对每一个lock锁都提供了一个Condition用来挂起和唤醒其他线程。

由于ArrayBlockingQueue采用的是数组的存储容器,因此在插入或删除元素时不会产生或销毁任何额外的对象实例,而LinkedBlockingQueue则会生成一个额外的Node对象。这可能在长时间内需要高效并发地处理大批量数据的时,对于GC可能存在较大影响。

构造方法:

 /**
     * 
     * 创造一个容量为Integer.MAX_VALUE的的对象
     */
    public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }


/**
     * 创造一个给定大小的队列.
     *
     * @param 队列大小
     * @throws IllegalArgumentException 如果参数不大于0
     *        
     */
    public LinkedBlockingQueue(int capacity) {
        if (capacity <= 0) throw new IllegalArgumentException();
        this.capacity = capacity;
        last = head = new Node<E>(null);
    }


  /**
     * 
     * 创造一个包含指定集和的队列,队列容量为Integer.MAX_VALUE,集合按照遍历顺序添加进队列
     *
     * @param c 指定集合
     * @throws NullPointerException 如果集合未申明或者集合元素为空
     *        
     */
    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;
            }
            //元素个数设置为n
            count.set(n);
        } finally {
        //释放入列锁
            putLock.unlock();
        }
    }

重要方法分析:

 /**
     * 唤醒出列线程,同时锁住出列线程,达到线程并发安全
     */
    private void signalNotEmpty() {
        final ReentrantLock takeLock = this.takeLock;
        //获得出列锁,达到线程并发安全
        takeLock.lock();
        try {
        //唤醒出列线程
            notEmpty.signal();
        } finally {
        //释放出列锁
            takeLock.unlock();
        }
    }

  /**
     * 唤醒入列线程,同时锁住入列线程
     */
    private void signalNotFull() {
        final ReentrantLock putLock = this.putLock;
          //获得出列锁,达到线程并发安全
        putLock.lock();
        try {
        //唤醒入列线程
            notFull.signal();
        } finally {
        //释放入列锁
            putLock.unlock();
        }
    }


//*********************************************入列方法*****************************************************


 /**
     * 将节点插入队尾.
     *
     * @param node the node
     */
    private void enqueue(Node<E> node) {
        // assert putLock.isHeldByCurrentThread();
        // assert last.next == null;
        last = last.next = node;
    }

  /**
     * 移除头节点.
     *
     * 返回头节点
     */
    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;
    }

 /**
     * 将元素添加至对尾巴, 如果队列慢了就阻塞等待可用空间
     *
     * @throws InterruptedException 线程打断异常
     * @throws NullPointerException 空指针异常
     */
    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;
        //取得入列锁,如果期间被其他线程打断报InterruptedException异常
        putLock.lockInterruptibly();
        try {
            /*
             * 当元素数量等于队列容量,阻塞入列线
             */
            while (count.get() == capacity) {
            //阻塞,直到notFull.signal();
                notFull.await();
            }
            //插入到队列
            enqueue(node);
            c = count.getAndIncrement();
            //如果队列元素个数小于容量,唤醒入列队列
            if (c + 1 < capacity)
                notFull.signal();
        } finally {
         //释放入列锁
            putLock.unlock();
        }
        if (c == 0)
            //只要有一个元素在队列,就唤醒出列线程
            signalNotEmpty();
    }
/**
     * 
     * 将元素添加至对尾巴, 如果队列慢了就阻塞等待指定时间直到有可用空间
     *
     * @return {@code true} 如果添加成功返回true
     * @throws InterruptedException 线程打断异常
     * @throws NullPointerException 空指针异常
     */
    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;
        //取得入列锁,如果期间被其他线程打断报InterruptedException异常
        putLock.lockInterruptibly();
        try {
            while (count.get() == capacity) {
            //指定时间不大于0直接返回false
                if (nanos <= 0L)
                    return false;
                    //队列满了阻塞指定时间
                nanos = notFull.awaitNanos(nanos);
            }
               //插入元素
            enqueue(new Node<E>(e));
            c = count.getAndIncrement();
            if (c + 1 < capacity)
            //元素个数小于容量,唤醒入列线程
                notFull.signal();
        } finally {
        //释放入列锁
            putLock.unlock();
        }
        if (c == 0)
         //只要有一个元素在队列,就唤醒出列线程
            signalNotEmpty();
        return true;
    }

 /**
     * 
     * 在队尾插入
     * 成功返回true,失败返回false
     *当使用容量受限队列时,此方法通常比方法add更可取,后者只能通过抛出异常才能插入元素。
     *
     * @throws NullPointerException 元素为空
     */
    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;
        Node<E> node = new Node<E>(e);
        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();
        }
        if (c == 0)
         //只要有一个元素在队列,就唤醒出列线程
            signalNotEmpty();
        return c >= 0;
    }


//*********************************************出列方法*****************************************************


 /**
     * 从对头移除元素
     *
     * @return 返回头节点
     */
    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;
    }


 /**
     * 从对头移除元素
     *
     * @return 返回头节点
     *如果被其他线程打断报InterruptedException
     */
public E take() throws InterruptedException {
        E x;
        int c = -1;
        //元素个数
        final AtomicInteger count = this.count;
        final ReentrantLock takeLock = this.takeLock;
        //获取出列锁,如果被其他线程打断报InterruptedException
        takeLock.lockInterruptibly();
        try {
            while (count.get() == 0) {
            //队列为空,阻塞出列线程
                notEmpty.await();
            }
            //删除并获取头节点
            x = dequeue();
            c = count.getAndDecrement();
            if (c > 1)
           //唤醒出列线程
                notEmpty.signal();
        } finally {
        //释放锁
            takeLock.unlock();
        }
        if (c == capacity)
        //只要元素数量小于容量,就唤醒入列线程
            signalNotFull();
        return x;
    }




    /**
     *
     *从对头删除元素并返回,如果队列为空等待指定时间再循环判断队列元素个数
     *如果时间参数错误,直接返回null
     *
     * @throws InterruptedException 间被其他线程打断报InterruptedException异常
     */
    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;
        //获取出列锁,期间被其他线程打断报InterruptedException异常
        takeLock.lockInterruptibly();
        try {
            while (count.get() == 0) {
                if (nanos <= 0L)
                   // 时间参数错误,直接返回null
                    return null;
                //如果队列为空,阻塞队列线程一段时间
                nanos = notEmpty.awaitNanos(nanos);
            }
            //删除并返回头节点
            x = dequeue();
            c = count.getAndDecrement();
            if (c > 1)
                //唤醒出列线程
                notEmpty.signal();
        } finally {
            //释放出列锁
            takeLock.unlock();
        }
        if (c == capacity)
            //只要元素数量小于容量,就唤醒入列线程
            signalNotFull();
        return x;
    }



/**
     *
     *从对头删除元素并返回,如果队列为空直接返回null
     *
     */
    public E poll() {
        //元素数量
        final AtomicInteger count = this.count;
        if (count.get() == 0)
            //队列为空返回null
            return null;
        E x = null;
        int c = -1;
        final ReentrantLock takeLock = this.takeLock;
        //获得出列锁
        takeLock.lock();
        try {
            if (count.get() > 0) {
                //删除并返回头节点
                x = dequeue();
                c = count.getAndDecrement();
                if (c > 1)
                    //唤醒出列线程
                    notEmpty.signal();
            }
        } finally {
            //释放出列锁
            takeLock.unlock();
        }
        if (c == capacity)
            //只要元素数量小于容量,就唤醒入列线程
            signalNotFull();
        return x;
    }

    public E peek() {
        if (count.get() == 0)
            return null;
        final ReentrantLock takeLock = this.takeLock;
        takeLock.lock();
        try {
            return (count.get() > 0) ? head.next.item : null;
        } finally {
            takeLock.unlock();
        }
    }


方法过多不一一说明如果需要查看请查阅官方文档https://docs.oracle.com/javase/8/docs/api/或者看源码

发布了23 篇原创文章 · 获赞 2 · 访问量 840

猜你喜欢

转载自blog.csdn.net/qq_34800986/article/details/104652933