LinkedBlockingQueue实现原理

1.LinkedBlockingQueue实现原理:源码分析如下

public class LinkedBlockingQueue<E> extends AbstractQueue<E>
    implements BlockingQueue<E>, java.io.Serializable {
	private static final long serialVersionUID = -6903933977591709194L;
	
    static class Node<E> {
        E item;       // 当前node(节点) 存放的值
        Node<E> next; // 指向下一个node节点
        Node(E x) { item = x; }
    }
	
    /** LinkedBlockingQueue队列的容量值,默认值为Integer.MAX_VALUE */
    private final int capacity;

    /** 当前元素记录数*/
    private final AtomicInteger count = new AtomicInteger(0);

    /**
     * 头节点
     * 头节点元素值为null(head.item == null)
     */
    private transient Node<E> head;

    /**
     * 最后一个节点.
     * 节点的下一个元素为null(last.next == null)
     */
    private transient Node<E> last;

    /** 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();
	
    /**
     * LinkedBlockingQueue构造方法
     * 容量大小为:Integer.MAX_VALUE
     */
    public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }
    /**
     * LinkedBlockingQueue构造方法
     * @param capacity 指定队列容量
     */
    public LinkedBlockingQueue(int capacity) {
        if (capacity <= 0) throw new IllegalArgumentException();
        this.capacity = capacity;
        last = head = new Node<E>(null);
    }
    
    public void put(E e) throws InterruptedException {
        if (e == null) throw new NullPointerException(); // 如果新添的元素为null,抛错
        int c = -1;
        Node<E> node = new Node(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(); // 当前元素+1,并将+1的前的值返回
            if (c + 1 < capacity)        // 如果当前容器的元素计数器个数小于队列的最大容量
                notFull.signal();        // 通知生产者,可继续生产加入元素,唤醒notFull.await();等待的线程
        } finally {
            putLock.unlock();            // 释放锁
        }
        if (c == 0) // 如果当前队列容量计数器为0(如:第一次添加元素)
            signalNotEmpty();
    }
    
    /**
     * 将最后一个节点元素指向新建的节点元素
     * @param node
     */
    private void enqueue(Node<E> node) {
        last = last.next = node;
    }
    
    /**
     * 唤醒notEmpty.await();等待的线程:如当前容器为0时,调用了 take()方法的线程
     */
    private void signalNotEmpty() {
        final ReentrantLock takeLock = this.takeLock; // 拿锁
        takeLock.lock(); // 尝试获取锁
        try {
            notEmpty.signal(); // 唤醒notEmpty.await();等待的线程
        } finally {
            takeLock.unlock(); // 释放锁
        }
    }
    
    /**
     * 取出第一个节点元素的值(头节点的下一个节点)
     */
    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(); // 当前容器计数器-1,并返回-1之前的数值
            if (c > 1)   // 如果当前元素记录器值大于1,则唤醒消费则 notEmpty.await();
                notEmpty.signal(); // C>1 表示当前容器个数已为0
        } finally {
            takeLock.unlock(); // 释放锁
        }
        if (c == capacity)  // 如果当前容器容量计数器的个数等于队列的最大容量,此时实际容量已小于最大容量,可生产
            signalNotFull(); // 唤醒put(E e)方法 调用 notFull.await();阻塞的线程(唤醒生产者开始生产)
        return x;
    }
    
    /**
     * 取出第一个节点(头节点的下一个节点)的元素值,
     * 并将头节点的下一个节点的元素值置为null
     * 然后将头节点指向原来头节点的下一个节点
     * @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;
    }
    
    /**
     * 唤醒生产则开始生产
     */
    private void signalNotFull() {
        final ReentrantLock putLock = this.putLock;
        putLock.lock();
        try {
            notFull.signal(); // 唤醒put(E e)方法 调用 notFull.await();阻塞的线程(唤醒生产者开始生产)
        } finally {
            putLock.unlock();
        }
    }
    
    
    
    
    

1.创建实例对象,调用LinkedBlockingQueue(int capacity)方法,内存图:


2.调用put(E e)方法里的 enqueue(Node<E> node) 方法中的 last.next = node; 内存图


3.调用put(E e)方法里的 enqueue(Node<E> node) 方法中的 last = last.next = node;内存图:


4.调用put(E e)方法里的 enqueue(Node<E> node) 继续添加下一个元素,last.next = node; 内存图


5..调用put(E e)方法里的 enqueue(Node<E> node) 继续添加下一个元素,last = last.next = node;内存图:


6.删除一个元素:调用E take()方法里的 E dequeue()方法 里的 Node<E> h = head; 内存图:


7.删除一个元素:调用E take()方法里的 E dequeue()方法 里的 Node<E> first = h.next; 内存图:


8.删除一个元素:调用E take()方法里的 E dequeue()方法 里的  h.next = h;  内存图:


9.删除一个元素:调用E take()方法里的 E dequeue()方法 里的  head = first; 内存图:


10.删除一个元素:调用E take()方法里的 E dequeue()方法 里的 first.item = null; 内存图:


总结:LinkedBlockingQueue链表是基于单项链表结构的队列,按照先进先出(FIFO)的原则取出数据。使用了两个重入锁来维护链表的添加和删除节点。

参考: 点击打开链接







猜你喜欢

转载自blog.csdn.net/qq_17089617/article/details/80892970