在 java生产者消费者专题---谈谈优化(三)中使用了一个锁对LinkedList进行锁定,实际可将队头和队尾各用一个锁,这样同时添加与移除元素时不会互相竞争,只有同时添加的线程或者同时移除的线程之间才会互相竞争。核心代码以下:java
static class Node<T> {
T val;
Node<T> next;优化
public Node(T val) {
super();
this.val = val;
}this
public Node(T val, Node<T> next) {
super();
this.val = val;
this.next = next;
}.net
}线程
private Node<T> headNode;
private Node<T> tailNode;设计
{blog
headNode =tailNode = new Node<T>(null);队列
}get
现分析队头与队尾节点能够分别采用不一样锁的缘由:源码
一、一开始take线程将因为队列为空而进入wait状态,put线程进行正常入队操做即tailNode=tailNode.next=newNode;
二、通过第一步后队列中有1个有效元素,且头节点指向一开始的哑节点,哑节点指向newNode,同时尾节点也指向newNode;
三、此时take线程被激活,与put线程同时处于活动状态,并假设此时只有一个take线程和一个put线程,它们会同时运行;
四、先说明take一个节点的步骤,因为头节点指向哑节点,所以哑节点指向的第一个节点是真正的数据节点,在获得数据节点的数据后,会将数据节点的数据域置空并将头节点指向它(此时该数据节点已经充当了新的哑节点角色,原先的哑吧节点会被废弃,具体能够看下LinkedBlockingQueue的源代码)。
五、经过第四步能够看出take时不会对数据节点的next域产生影响,而put时仅仅对最后一个数据节点的next域产生影响,因此head节点和tail节点能够采用不一样的锁,即take线程和put线程是永远不会发生竞争的!
哑节点的设计是实现该方法的核心部分,源码你们就参考LinkedBlockingQueue便可,下篇将分析与ArrayBlockingQueue的效率差别!