LinkedBlockingQueue source code analysis

Disclaimer: This article is a blogger original article, welcome to reprint. https://blog.csdn.net/guo_xl/article/details/84942404

LinkedBlockingQueue following structure, essentially a single linked list

node(head)->node->node->node(last)

static class Node<E> {
        E item;
        Node<E> next;
        Node(E x) { item = x; }
    }

The constructor is very simple

public LinkedBlockingQueue(int capacity) {
        if (capacity <= 0) throw new IllegalArgumentException();
        this.capacity = capacity;
        last = head = new Node<E>(null);
}

LinkedBlockingQueue and the difference ArrayBlockingQueue

  • ArrayBlockingQueue using an array data structure, LinkedBlockingQueue single chain
  • LinkedBlockingQueue double locks, and locks into the read lock and single lock ArrayBlockingQueue, and placed in a shared read lock. This determines the ArrayBlockingQueue into and read mutually exclusive, but LinkedBlockingQueue largely not only in the queue is full or empty for the extreme cases will be mutually exclusive. Such efficiency is higher than ArrayBlockingQueue.

The following analysis of LinkedBlockingQueue put (offer) and take (get) a

put()

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 {
           //如果已经达到capacity,表示队列已经满了,阻塞等待
            while (count.get() == capacity) {
                notFull.await();
            }
            //元素入队列
            enqueue(node);
            c = count.getAndIncrement();
            //有机会就判断下是否已经队列不满条件满足,如果是则唤醒,
            //如果这里还是不满足,则只能在take()里唤醒
            
            //为什么这里是c+1<capacity 而不是c<capacity?原因在于getAndIncrement是返回的未加之前的值,
            if (c + 1 < capacity)
                notFull.signal();
        } finally {
            putLock.unlock();
        }
        //c == 0,c是加之前的值,如果c == 0,实际上队列里至少有一个元素。
        if (c == 0)
            signalNotEmpty();
    }

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();
            c = count.getAndDecrement();
           //这里为什么c>1而不是c>0?因为count.getAndDecrement()返回的是没减之前的值,而能取代表至少count是1。
           //如果count减完还要非空,count至少是2,所以c>1。
            if (c > 1)
                notEmpty.signal();
        } finally {
            takeLock.unlock();
        }
        //c == capacity,c是减完之前的值,如果c == capacity,实际上队列里至少有一个空位。
        if (c == capacity)
            signalNotFull();
        return x;
    }

clear()

public class LinkedBlockingQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable {

    public void clear() {
        fullyLock();
        try {
            for (Node<E> p, h = head; (p = h.next) != null; h = p) {
            //未什么h.next = h而不是h.next=null?
                h.next = h;
                p.item = null;
            }
            head = last;
            // assert head.item == null && head.next == null;
            if (count.getAndSet(0) == capacity)
                notFull.signal();
        } finally {
            fullyUnlock();
        }
    }            
            
}

Execution is the result of clear traversing the node where the item blank and need to disconnect each node of the next, in fact node.next point to myself, there is clever use of the for loop.

for(表达式1;表达式2;表达式3)
{
    //循环体
}

First execution "Expression 1", then "Expression 2" is determined, the implementation of the determination is true "loop", the loop body is executed 3. After executing the expression
"Expression 1 only once

Why h.next = h instead h.next = null? In the following method in fact help GC, the next point to make their own, should help GC, to know the results do not know why.

 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;
    }

Guess you like

Origin blog.csdn.net/guo_xl/article/details/84942404