LinkedBlockingQueue源码分析

来源:互联网 发布:网络电工是什么 编辑:程序博客网 时间:2024/05/19 09:40

几乎JDK中所有以linked开头的类(LinkedBlockingDeque、LinkedList、LinkedHashMap)等,其内部都是以链表做支撑。

现在我们来看看LinkedBlockingQueue的源码,了解其如何去实现阻塞队列的。

LinkedBlockingQueue在内部维护了一个Node对象,对象存放这当前节点跟下一个节点

   static class Node<E> {    //当前节点存放的        E item;        /**         * - 指向下一个节点         * - 指向当前节点,意味着这事头节点的下一个节点         * - 如果为空,意味着当前节点为尾节点         */        Node<E> next;        Node(E x) { item = x; }    }

//队列容量    private final int capacity;    //当前队列长度    private final AtomicInteger count = new AtomicInteger();

队列里面维护着一个头节点(方便出队操作)跟最后一个节点(方便入队操作)

/**     * 头节点, head.item 固定为  null     */    transient Node<E> head;    /**     * 尾节点,last.next固定 null     */    private transient Node<E> last;

LinkedBlockingQueue里面最核心的莫过于出队跟入队这两个锁,以及其所对应的Condition,其阻塞都是通过这两个取实现的

    /**元素出队列的锁 */    private final ReentrantLock takeLock = new ReentrantLock();    /** 队列为空时,notEmpty进入await状态,此时从队列取元素的操作阻塞着,知道其他地方往队列存如元素。存放时会调用signal */    private final Condition notEmpty = takeLock.newCondition();    /** 元素进队列的锁 */    private final ReentrantLock putLock = new ReentrantLock();    /** 队列满时,notEmpty进入await状态,此时从队列存元素的操作阻塞着,直到其他地方从队列取元素。取元素时会调用signal */    private final Condition notFull = putLock.newCondition();

 /**     * 唤醒一个取元素的线程,只有在put或offer方法中调用(往队列里放了元素,此时队列肯定不为空,就可以唤醒取元素的线程)     * 用于一开始队列为空,首次往里面放入元素的时候     */    private void signalNotEmpty() {        final ReentrantLock takeLock = this.takeLock;        takeLock.lock();        try {            notEmpty.signal();//用锁保护是为了在唤醒取元素线程时,不会有其他线程从队列取走元素,导致队列为空        } finally {            takeLock.unlock();        }    }    /**     * 唤醒存放元素线程. 只能由take/poll调用(从队列取走元素,此时队列肯定不是满的,就可以唤醒存放元素的线程)     * 用于当前队列满,取走一个元素之后调用     */    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;    }    /**     * 出队列操作     *     * @return the node     */    private E dequeue() {        // assert takeLock.isHeldByCurrentThread();        // assert head.item == null;        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;    }

下面这两个方法主要是某些特殊操作(清空队列,移除元素等)时控制整个队列不能出队入队

    /**     * put、take操作全锁住     */    void fullyLock() {        putLock.lock();        takeLock.lock();    }    /**     * put、take操作全解锁     */    void fullyUnlock() {        takeLock.unlock();        putLock.unlock();    }

/**     * 当我们new一个LinkedBlockingQueue,默认队列容量的int的最大值     */    public LinkedBlockingQueue() {        this(Integer.MAX_VALUE);    }    /**     *初始化的时候,头、尾节点是同一个节点,且节点元素为空     */    public LinkedBlockingQueue(int capacity) {        if (capacity <= 0) throw new IllegalArgumentException();        this.capacity = capacity;        last = head = new Node<E>(null);    }    /**     * 实例化的时候传入Collection,这种比较少用     */    public LinkedBlockingQueue(Collection<? extends E> c) {        this(Integer.MAX_VALUE);        final ReentrantLock putLock = this.putLock;        putLock.lock(); // Never contended, but necessary for visibility        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();        }    }

    /**     * 返回队列长度     */    public int size() {        return count.get();    }    /**     * 剩余容量     */    public int remainingCapacity() {        return capacity - count.get();    }

入队相关的操作,有阻塞方法,也有非阻塞方法

/**     * 尾插入(队列先进先出),阻塞的方法     */    public void put(E e) throws InterruptedException {        if (e == null) throw new NullPointerException();        // Note: convention in all put/take/etc is to preset local var        // holding count negative to indicate failure unless set.        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();//注意此处getAndIncrement是先取当前值,之后count再+1            if (c + 1 < capacity)                notFull.signal();        } finally {            putLock.unlock();        }        if (c == 0)//一开始队列空的时候,c == 0            signalNotEmpty();//唤醒取线程    }    /**     * 跟上面put差不多,只是多了超时插入失败操作,阻塞的方法(timeout时间内)     */    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) {                if (nanos <= 0)                    return false;                nanos = notFull.awaitNanos(nanos);//awaitNanos若指定时间内收到signal()或signalALL()则返回nanos减去已经等待的时间;            }            enqueue(new Node<E>(e));            c = count.getAndIncrement();            if (c + 1 < capacity)                notFull.signal();        } finally {            putLock.unlock();        }        if (c == 0)            signalNotEmpty();        return true;    }    /**     * 跟put差不多,只是offer方法如果存元素的时候队列满,立即失败,非阻塞的方法     */    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 {        //如果队列没满,node入队,c得到赋值            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;//队列满的时候,此处c=-1,返回false    }

出队操作

/**     * 取元素,阻塞的方法     */    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();            c = count.getAndDecrement();            if (c > 1)                notEmpty.signal();        } finally {            takeLock.unlock();        }        if (c == capacity)            signalNotFull();        return x;    }    /**     * 同样是取元素,多了超时失败操作,阻塞的方法(timeout时间内)     */    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) {                if (nanos <= 0)                    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)            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 {            Node<E> first = head.next;            if (first == null)                return null;            else                return first.item;        } finally {            takeLock.unlock();        }    }

移除节点的操作

  /**     * 用p的前一个节点来移除内部节点p(画解)     */    void unlink(Node<E> p, Node<E> trail) {        // assert isFullyLocked();        // p.next is not changed, to allow iterators that are        // traversing p to maintain their weak-consistency guarantee.        p.item = null;        trail.next = p.next;        if (last == p)            last = trail;        if (count.getAndDecrement() == capacity)            notFull.signal();    }    /**     * 移除一个节点(移除的时候不允许从队列存、取元素)     */    public boolean remove(Object o) {        if (o == null) return false;        fullyLock();        try {            for (Node<E> trail = head, p = trail.next;                 p != null;                 trail = p, p = p.next) {                if (o.equals(p.item)) {                    unlink(p, trail);                    return true;                }            }            return false;        } finally {            fullyUnlock();        }    }

/**     * 判断队列是否存在元素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();        }    }    /**     * 队列转数组(操作时不允许从队列存、取元素)     */    public Object[] toArray() {        fullyLock();        try {            int size = count.get();            Object[] a = new Object[size];            int k = 0;            for (Node<E> p = head.next; p != null; p = p.next)                a[k++] = p.item;            return a;        } finally {            fullyUnlock();        }    }    /**     * 转成特定类型数组     */    @SuppressWarnings("unchecked")    public <T> T[] toArray(T[] a) {        fullyLock();        try {            int size = count.get();            if (a.length < size)                a = (T[])java.lang.reflect.Array.newInstance                    (a.getClass().getComponentType(), size);            int k = 0;            for (Node<E> p = head.next; p != null; p = p.next)                a[k++] = (T)p.item;            if (a.length > k)                a[k] = null;            return a;        } finally {            fullyUnlock();        }    }    public String toString() {        fullyLock();        try {            Node<E> p = head.next;            if (p == null)                return "[]";            StringBuilder sb = new StringBuilder();            sb.append('[');            for (;;) {                E e = p.item;                sb.append(e == this ? "(this Collection)" : e);                p = p.next;                if (p == null)                    return sb.append(']').toString();                sb.append(',').append(' ');            }        } finally {            fullyUnlock();        }    }    /**     * 原子的清空队列(画图)     */    public void clear() {        fullyLock();        try {            for (Node<E> p, h = head; (p = h.next) != null; h = p) {                h.next = h;                p.item = null;            }            head = last;            // assert head.item == null && head.next == null;            if (count.getAndSet(0) == capacity)                notFull.signal();        } finally {            fullyUnlock();        }    }

一般常用的方法就是上面的代码,余下的方法不做解读。接着用图来看看队列是如何处理的。

LinkedBlockingQueue里面的数据结构是这样的,Node对象保存着当前元素,还有下一个元素


当我们去实例化一个LinkedBlockingQueue的时候,队列中head、last指向同一个元素为空的Node节点


当往队列里放元素的时候,head位置不变,之后将head的next节点指向新增的节点,同时last也指向新增的节点

当往队列取元素的时候,是这样一个过程



到这,队列里面的代码以及出队入队的操作应该就相对清晰很多了,有些人可能会有疑问,为什么出队、入队操作里面都会有一个while循环,之后是await

 while (count.get() == capacity) {                notFull.await();            }

其实是这样的:await方法调用之后,当前线程会释放锁,知道其他线程调signal方法。但是signal方法每次只是随机的唤醒一个线程。那么,如果唤醒之后依然不满足条件,那么它还得继续等,知道条件满足(此处对应:队列满的时候,多个入队线程阻塞着,之后队列取走一个元素,有一个入队线程被唤醒,但它还没执行的时候,又有另一个线程入队,这时队列又满了,被唤醒的队列不满足 条件,还得继续等待)


signalNotEmpty()跟signalNotFull()为什么都在特殊情况下使用?

以signalNotEmpty为例:

队列一开始为空的时候,有多个线程来取元素,那么这些线程就都阻塞着,当往队列里面存入元素时,如果不去唤醒这些线程,在没有薪线程来取元素的情况下,这些线程就永远阻塞着,无论之后队列里又新增了多少个元素

原创粉丝点击