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

06-09 11:10