LinkedBlockingQueue源碼詳解

一)LinkedBlockingQueue<E>簡介

LinkedBlockingQueue<E>源碼詳解(基於jdk1.8.0_162)

1)底層由單鏈表實現。

2)是一個FIFO(先進先出)的阻塞隊列。

3)創建時,可指定隊列初始容量,如未指定,默認爲Integer.MAX_VALUE,指定後不能修改。是一個線程安全的隊列。

 

類圖:LinkedBlockingQueue<E>繼承了AbstractQueue<E>類並實現了BlockingQueue<E>接口。

 

二)構造方法

1)LinkedBlockingQueue()

創建一個 LinkedBlockingQueue ,容量爲 Integer.MAX_VALUE 。

2)LinkedBlockingQueue(int capacity)

 創建一個具有給定(固定)容量的 LinkedBlockingQueue 。

public LinkedBlockingQueue() {
    this(Integer.MAX_VALUE); // 默認Integer最大容量
}

public LinkedBlockingQueue(int capacity) {
    if (capacity <= 0) throw new IllegalArgumentException();
    this.capacity = capacity; // 初始化隊列容量
    last = head = new Node<E>(null); // 把隊列頭尾相連並初始化爲null
}

 

三)添加

1)void put(E e)

 在該隊列的尾部插入指定的元素,如果需要,等待空格變爲可用。

public void put(E e) throws InterruptedException {
    if (e == null) throw new NullPointerException(); // 元素不能爲null
        
    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(); // 隊列數量累計1
        if (c + 1 < capacity) // 如果隊列實際容量小於固定容量,喚醒生產者線程
            notFull.signal();
    } finally {
        putLock.unlock(); // 解鎖
    }
    if (c == 0)
    signalNotEmpty(); // 喚醒消費者線程
}

private void enqueue(Node<E> node) {
    // assert putLock.isHeldByCurrentThread();
    // assert last.next == null;
    last = last.next = node; // 把新節點添加在隊列尾部
}

 

四)查找

1)boolean contains(Object o)

如果此隊列包含指定的元素並存在多個,查找到第一個元素之後就返回 true,否則返回false。

public boolean contains(Object o) {
    if (o == null) return false; // 對象爲空, 返回false
    fullyLock(); // 加鎖
    try {
        for (Node<E> p = head.next; p != null; p = p.next) // 從頭節點開始循環查找元素
            if (o.equals(p.item)) // 查找到第一個元素,直接返回true
                return true;
            return false; // 未找到元素, 返回false
    } finally {
        fullyUnlock(); // 解鎖
    }
}

 

2)E peek()

檢索但不刪除此隊列的頭,如果此隊列爲空,則返回 null 。

public E peek() {
    if (count.get() == 0) // 如果隊列爲空, 則返回null
        return null;
    final ReentrantLock takeLock = this.takeLock;
    takeLock.lock();
    try {
        Node<E> first = head.next; // 獲取頭部節點
        if (first == null) // 如果頭部節點爲null, 則返回null
            return null;
        else
            return first.item; // 返回頭部節點的item
    } finally {
        takeLock.unlock();
    }
}

 

3)E poll()

檢索並刪除此隊列的頭,如果此隊列爲空,則返回 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(); // 隊列數量累減1
            if (c > 1) // 如果隊列數量大於1, 喚醒消費者線程
                notEmpty.signal();
        }
    } finally {
        takeLock.unlock();
    }
    if (c == capacity)
        signalNotFull(); // 喚醒生產者線程
    return x; // 返回的元素信息
}

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; // 返回原頭部節點信息
}

 

4)E 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) { // 如果隊列爲null,則一直阻塞,等待生產者生產元素,再喚醒消費者線程
            notEmpty.await();
        }
        x = dequeue(); // 獲取隊列頭部節點
        c = count.getAndDecrement(); // 隊列數量累減1
        if (c > 1)
            notEmpty.signal(); // 當隊列數量大於1時,喚醒消費者線程
    } finally {
        takeLock.unlock();
    }
    if (c == capacity)
        signalNotFull(); // 喚醒生產者線程
    return x;
}

 

五)刪除

1)void clear()

從這個隊列中原子地刪除所有的元素。

public void clear() {
    fullyLock();
    try {
        for (Node<E> p, h = head; (p = h.next) != null; h = p) { // 把隊列元素循環置null
            h.next = h;
            p.item = null;
        }
        head = last; // 把頭部節點和尾部節點相連
        // assert head.item == null && head.next == null;
        if (count.getAndSet(0) == capacity) // 把隊列數量初始化爲0
            notFull.signal(); // 喚醒生產者線程
    } finally {
        fullyUnlock();
    }
}

 

2)boolean remove(Object o)

從該隊列中刪除指定元素的單個實例(如果存在)。

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)) { // 從頭部節點開始,循環比較元素,如找到元素,先刪除,再返回true
                unlink(p, trail);
                return true;
            }
        }
        return false; // 未找到元素,返回false
    } finally {
        fullyUnlock();
    }
}

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; // 把找到的元素置null
    trail.next = p.next; // 臨時存儲刪除元素的下一節點
    if (last == p) // 如果刪除元素是尾部元素
        last = trail; // 重新指定尾部元素
    if (count.getAndDecrement() == capacity) // 隊列數量累減1
        notFull.signal(); // 喚醒生產者線程
}

 

識別二維碼關注個人微信公衆號

本章完結,待續,歡迎轉載!
 
本文說明:該文章屬於原創,如需轉載,請標明文章轉載來源!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章