浅谈Java AQS实现——CLH锁

  在AQS源码的注释中提到AQS是CLH锁的一个变种,所以了解一下CLH锁有助于我们学习AQS。CLH锁是基于链表的、公平的的自旋锁。

这里直接贴代码(代码来自《多处理器编程的艺术》机械工业出版社)

// 这里只实现了Lock接口的部分方法
public class CLHLock implements Lock {
    AtomicReference<QNode> tail = new AtomicReference<QNode>(new QNode());
    ThreadLocal<QNode> myPred;
    ThreadLocal<QNode> myNode;

    public CLHLock() {
        // 队列有一个默认的头节点
        tail = new AtomicReference<QNode>(new QNode());
        myNode = new ThreadLocal<QNode>() {
            @Override
            protected QNode initialValue() {
                return new QNode();
            }
        };
        myPred = new ThreadLocal<QNode>() {
            @Override
            protected QNode initialValue() {
                return null;
            }
        };
    }

    @Override
    public void Lock() {
        QNode qnode = myNode.get();
        qnode.locked = true;
        // 原子的进入队列
        QNode pred = tail.getAndSet(qnode);
        myPred.set(pred);
        // 在前驱节点的locked字段上自旋,第一个线程则会检测默认的头节点的locked的值
        while (pred.locked) {
        }
    }

    @Override
    public void Unlock() {
        QNode qnode = myNode.get();
        // 放弃锁,后驱节点可以继续
        qnode.locked = false;
        // 这个有点绕,把当前线程的myNode引用设置成队列的头节点,前线程的myNode的原引用则变成队列的头结点。前线程的myNode则会从队列中脱离。
        myNode.set(myPred.get());
    }

    // 在书中没有给出QNode的定义
    private class QNode {
        // 不知道作者的原意是在这里使用volatile来实现锁的内存语义还是通过其他的方式。
        public volatile boolean locked = false;
    }
}

  CLH锁的核心在于线程在他对应节点的前驱节点上自旋,等待前驱节点放弃锁的通知,CLH锁在NUMA系统结构下表现差也是因为此,AQS的实现与CLH锁相似的地方也在于此。在AQS中线程获取失败会进入等待队列,阻塞并等待前驱节点对应线程的唤醒。

猜你喜欢

转载自www.cnblogs.com/xtaieer/p/10725325.html