LinkedBlockingQueue源碼簡析

LinkedBlockingQueue是一個基于鏈表的阻塞隊列,實現了BlockingQueue、java.io.Serializable接口繼承自AbstractQueue

創建:

//當隊列中沒有任何元素的時候,此時隊列的頭部就等于隊列的尾部,指向的是同一個節點,并且內容為null
public LinkedBlockingQueue(int capacity) {
    if (capacity <= 0) throw new IllegalArgumentException();
    this.capacity = capacity;
    last = head = new Node<E>(null);
}
//無參構造,默認使用int的最大值作為capacity的值
public LinkedBlockingQueue() {
    this(Integer.MAX_VALUE);
}
//將一個集合中的元素全部入隊列。
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分析        
                enqueue(new Node<E>(e));
                ++n;
        }
        count.set(n);
    } finally {
    //釋放鎖
    putLock.unlock();
    }
}   

數據元素操作:

//我們看到是一個鏈表,里面每個節點都是一個內部類Node,所有的元素都通過Node類來進行存儲

static class Node<E> {
    E item;
    
    Node<E> next;

    Node(E x) { item = x; }
    }
//創建一個節點,并加入鏈表尾部,入隊
private void enqueue(Node<E> node) {
    //把node賦給當前的最后一個節點的下一個節點,然后在將node設為最后一個節點
    last = last.next = node;
}
//出隊
private E dequeue() {
    Node<E> h = head;//獲取頭節點:x==null
    Node<E> first = h.next;//將頭節點的下一個節點賦值給first
    h.next = h; // 將當前將要出隊的節點置null(為了使其做head節點做準備
    head = first;//將當前將要出隊的節點作為了頭節點
    E x = first.item;//獲取出隊節點的值
    first.item = null;//將出隊節點的值置空
    return x;
}

重要參數:


    //AtomicInteger 一個提供原子操作的Integer的類,用來計算現在隊列有多少個元素
    //解決在入隊或出隊時并發修改元素數量
    private final AtomicInteger count = new AtomicInteger(0);

    //元素出隊入隊時線程所獲取的重入鎖
    private final ReentrantLock takeLock = new ReentrantLock();
    private final ReentrantLock putLock = new ReentrantLock();


    //當隊列為空時,讓從隊列中獲取元素的線程處于掛起狀態
    private final Condition notEmpty = takeLock.newCondition();
    //當隊列為空時,讓元素入隊列的的線程處于掛起狀態
    private final Condition notFull = putLock.newCondition();

入隊:

 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
        
        int c = -1;
        Node<E> node = new Node(e);
        //當成員變量被其他線程改變時,因為在方法內部重新引用并用final修飾,保證在一次操作內數據是一致的?
        final ReentrantLock putLock = this.putLock;
        final AtomicInteger count = this.count;
        //執行可中斷的鎖獲取操作
        putLock.lockInterruptibly();
        try {
           
            //當隊列的容量到底最大時,此時線程將處于等待狀態 ?為什么要使用while
            while (count.get() == capacity) {
                notFull.await();
            }
            //讓元素排入隊列的末尾
            enqueue(node);
            //更新隊列中的元素個數.此處的count.getAndIncrement()方法會更新元素個數并返回舊值
            c = count.getAndIncrement();
            //如果添加元素后的容量,還小于指定容量,說明至少還可以再插一個元素
            if (c + 1 < capacity)
                notFull.signal();
        } finally {
            //釋放鎖
            putLock.unlock();
        }
        
        //注意!!!這個c的值就count容量的舊值,c == 0時說明之前的隊列是空隊列,即出隊列=的線程都處于等待狀態
        //上邊增加了一個新的元素,隊列不為空,就會喚醒正在等待獲取元素的線程
        if (c == 0)
            signalNotEmpty();
    }


    //喚醒正在等待獲取元素的線程
    private void signalNotEmpty() {
        final ReentrantLock takeLock = this.takeLock;
        takeLock.lock();
        try {
            //通過notEmpty喚醒獲取元素的線程
            notEmpty.signal();
        } 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,喚醒線程,繼續執行出隊操作
            if (c > 1)
                notEmpty.signal();
        } finally {
            //釋放鎖
            takeLock.unlock();
        }
       
        //c==capaitcy的時候,在執行此次出隊操作完成之前隊列已經滿了,去喚醒入隊操作的線程進行插入操作
        if (c == capacity)
            signalNotFull();
        return x;
    }
    
    
     private void signalNotFull() {
        final ReentrantLock putLock = this.putLock;
        putLock.lock();
        try {
            notFull.signal();
        } finally {
            putLock.unlock();
        }
    }


補充:

AtomicInteger

對于一個Integer線程安全的操作。

//當前值+1,采用無限循環,直到+1成功為止
//返回的是舊值
public final int getAndIncrement() {
        for (;;) {
            int current = get();//獲取當前值
            int next = current + 1;//當前值+1
            if (compareAndSet(current, next))//基于CAS賦值
                return current;
    }
}
    
//當前值-1,采用無限循環,直到-1成功為止 
//返回舊值
public final int getAndDecrement() {
    for (;;) {
        int current = get();
        int next = current - 1;
            if (compareAndSet(current, next))
                return current;
    }
}
    

compareAndSet這個方法多見于并發控制中,簡稱CAS(Compare And Swap),意思是如果valueOffset位置包含的值與expect值相同,則更新valueOffset位置的值為update,并返回true,否則不更新,返回false。

public final boolean compareAndSet(int expect, int update) {
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容