CLH锁—AQS的核心原理

CLH的核心是:当前节点自旋前一个节点的状态locked状态。当前一个节点locked状态是false时,当前节点可以从自旋状态退出执行后续方法。自旋的节点无法执行后续逻辑,代表未获得锁。退出自旋的节点可以执行后续逻辑表示已获取锁。

每次需要尝试获取锁的节点locked状态都设置true(解释一下当前节点locked的状态为什么要设置成true,locked状态是给后继节点自旋检测用的,当前节点都在尝试获取锁,后继节点一定不能获得锁,需要进行自旋;通俗的讲就是狗大哥都没吃饱,其他狗小弟更不能吃)。

并且将该节点设置成末尾节点,然后返回之前的末尾节点,返回后当前节点检测返回的末尾节点的locked状态,如果状态是false则不需要自旋,如果是true则需要自旋。依此类推......

假如我现在依次添加的节点A,B,C,D。那么最开始A是设置成末尾节点,并返回之前tail节点(初始化了一个tail节点,可以看下面的源码),A检测tail节点locked状态。

然后是B是末尾节点,B的前一个末尾节点是A,那么B检测A的locked状态。

C检测B的locked状态。D检测Alocked状态。

node1就是A,node2就是B,node3就是C,node4没写(真写不下了)。

package ltd.wzrj.lock;

import java.util.concurrent.atomic.AtomicReference;

public class CLHLock {
    // node每个线程都有一个副本
    private static final ThreadLocal<Node> node = ThreadLocal.withInitial(Node::new);
    private static final AtomicReference<Node> tail = new AtomicReference<>(new Node());

    static final class Node {
        private volatile boolean locked;
        Node() {
            this.locked = false;
        }
    }

    /**
     * 尝试获得锁
     */
    public void tryLock() {
        // 从当前线程ThreadLocal获取当前节点
       Node node1 = this.node.get();
       // 设置当前节点为站锁
       node1.locked = true;
       // 设置尾部节点 并返回之前的尾节点
       Node pre =this.tail.getAndSet(node1);
       // 当前节点判断之前的尾部节点状态,如果是false则获得锁成功,从自旋中醒来
       while(pre.locked){
           System.out.println(Thread.currentThread().getName()+"等待中...");
           try {
               Thread.sleep(3000);
           } catch (InterruptedException e) {
               throw new RuntimeException(e);
           }
       }
    }

    /**
     * 释放锁
     */
    public void unlock() {
        // 从当前线程ThreadLocal获取当前节点
        Node Node = this.node.get();
        // 设置当前节点的状态是为解锁状态,后面自旋的线程就能检测
        Node.locked = false;
        /* 重置该节点,避免该节点再次获得锁后,造成死锁。
        (解释一下为什么能该节点还能获得锁,并造成死锁。因为在lock方法中,判断的是当前节点的前一个节点状态。
        当前节点能获得锁后,释放锁说明当前节点的前一个节点是状态是false,
        那么当当前节点再次执行lock方法时也可以执行lock方法,所以执行lock方法后,会把这个节点添加到尾节点之后,状态改成true
        【如果添加到尾节点,检测尾节点的状态,进入自旋,那么当前节点的后面的一个节点(命名B)就有两种情况:
        1、B获得锁。当我当前线程释放锁之后,B在当前节点进入lock方法之前马上获得锁,程序正常运行。
        2、B没有获得锁。当当前线程释放锁之后,B节点没有获得锁,那么B节点还在等我当前节点状态改成false,
        但是我当前节点已经执行了lock方法,locked变成了true,进入自旋,
        那么在B的后续节点中就没有locked为false的节点了,造成了所有节点一直自旋,无解。】)
        如果我新的当前节点,那么即使重新进入了lock方法,新的节点会到尾部节点,而旧的节点不变,B还是指向旧的节点


         */
        this.node.set(new Node());

    }
}




public class CLHLockTest {
    private  final CLHLock clhLock = new CLHLock();

    private void testLock() {
        // 加锁
        clhLock.lock();
        try {
            System.out.println(Thread.currentThread().getName() +"获得锁"  );
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            // 解锁
            clhLock.unlock();
        }
    }

    public static void main(String[] args) {
        CLHLockTest instance = new CLHLockTest();
        // 启动100线程做自增操作
        IntStream.range(0, 2).forEach(e -> new Thread(instance::testLock).start());
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值