在 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的效率差异!