java生產者消費者專題---談談優化(四)
在 java生產者消費者專題---談談優化(三)中使用了一個鎖對LinkedList進行鎖定,實際可將隊頭和隊尾各用一個鎖,這樣同時新增與移除元素時不會互相競爭,只有同時新增的執行緒或者同時移除的執行緒之間才會互相競爭。核心程式碼如下:
static class Node<T> {
T val;
Node<T> next;
public Node(T val) {
super();
this.val = val;
}
public Node(T val, Node<T> next) {
super();
this.val = val;
this.next = next;
}
}
private Node<T> headNode;
private Node<T> tailNode;
{
headNode =tailNode = new Node<T>(null);
}
現分析隊頭與隊尾節點可以分別採用不同鎖的原因:
1、一開始take執行緒將由於佇列為空而進入wait狀態,put執行緒進行正常入隊操作即tailNode=tailNode.next=newNode;
2、經過第一步後佇列中有1個有效元素,且頭節點指向一開始的啞節點,啞節點指向newNode,同時尾節點也指向newNode;
3、此時take執行緒被啟用,與put執行緒同時處於活動狀態,並假設此時只有一個take執行緒和一個put執行緒,它們會同時執行;
4、先說明take一個節點的步驟,由於頭節點指向啞節點,因此啞節點指向的第一個節點是真正的資料節點,在得到資料節點的資料後,會將資料節點的資料域置空並將頭節點指向它(此時該資料節點已經充當了新的啞節點角色,原先的啞巴節點會被廢棄,具體可以看下LinkedBlockingQueue的原始碼)。
5、通過第四步可以看出take時不會對資料節點的next域產生影響,而put時僅僅對最後一個數據節點的next域產生影響,所以head節點和tail節點可以採用不同的鎖,即take執行緒和put執行緒是永遠不會發生競爭的!
原始碼大家就參考LinkedBlockingQueue即可,下篇將分析與ArrayBlockingQueue的效率差