2017-04-26 57 views
1

这是一个典型的CLH锁在java中:为什么CLH锁需要在Java上一个节点

public class CLHLock{ 

    private final AtomicReference tail; 

    // why we need this node? 
    private final ThreadLocal myPred; 

    private final ThreadLocal myNode; 

    public CLHLock() { 
     tail = new AtomicReference(new QNode()); 
     myNode = new ThreadLocal() { 
      protected QNode initialValue() { 
       return new QNode(); 
      } 
     }; 

     myPred = new ThreadLocal(); 
    } 

    public void lock() { 
     QNode node = myNode.get(); 
     node.locked = true; 
     QNode pred = tail.getAndSet(node); 

     // this.myPred == pred 
     myPred.set(pred); 
     while (pred.locked) { 
     } 
    } 

    public void unlock() { 
     QNode node = myNode.get(); 
     node.locked = false; 

     // this.myNode == this.myPred 
     myNode.set(myPred.get()); 
    } 

    private static class QNode { 
     volatile boolean locked; 
    } 
} 

我们为什么需要myPred节点,只有两个地方使用这个变量:

  1. this.prev.set(pred);
  2. 列表项this.node.set(this.prev.get());

,当我们做了,this.prev == this.node == pred

也许我们可以实现这样的:

public class CLHLock { 
    // Node tail 
    private final AtomicReference<QNode> tail = new AtomicReference<>(new QNode()); 

    // ThreadLocal 
    private final ThreadLocal<QNode> node = ThreadLocal.withInitial(QNode::new); 

    public void lock() { 
     QNode now = node.get(); 
     now.locked = true; 

     // spin on pre-node 
     QNode pre = tail.getAndSet(now); 
     while (pre.locked) { 
     } 
    } 

    public void unlock() { 
     QNode now = node.get(); 
     now.locked = false; 
    } 

    class QNode { 
     volatile boolean locked = false; 
    } 
} 

是什么上述两者的区别?

回答

2

第二个实现是死锁倾向。

假设你有两个线程T1和T2。 T1拥有锁,T2等待T1释放它。

T1.node.locked是真实的,T2.node.locked是真实的,尾指向T2.node和T2是纺纱上pre.locked,这是T1的节点。

现在T1释放锁定(设置T1.node.locked为false),并在此之后尝试再次获取它,同时T2被抢占。 T1.node.locked再次变为真,但尾部为T2.node,所以T1现在正在等待T2。而T2仍在等待现在被锁定的T1的同一个节点!僵局。

第一个实现通过重复使用不是当前的,但前一个(前驱)节点来保护你,因此这种情况是不可能的:或者前驱是null(那么没有什么可重用)或者不是,那么当节点被重用时它变得解锁。

相关问题