ReentranLock源码解析

AQS是Java中的AbstractQueueSynchronizer类,底层维护了一个双向链表存储未获取到资源的线程。

一.Sync类

静态内部类Sync继承了AQS类,AQS底层维护了一个Node静态内部类。

    static final class Node {

        static final Node SHARED = new Node();

        // 独占锁的标识
        static final Node EXCLUSIVE = null;

        // 如果有这个标识,代表失效了
        static final int CANCELLED = 1;

        // 如果有这个标识,说明后继节点需要被唤醒
        static final int SIGNAL = -1;

        static final int CONDITION = -2;

        static final int PROPAGATE = -3;

        // Node对象存储标识的地方
        volatile int waitStatus;

        // 前驱节点
        volatile Node prev;

        // 后继节点
        volatile Node next;

        // 当前Node绑定的线程
        volatile Thread thread;

        Node nextWaiter;

        final boolean isShared() {
            return nextWaiter == SHARED;
        }

        // 返回前驱节点
        final Node predecessor() throws NullPointerException {
            Node p = prev;
            if (p == null)
                throw new NullPointerException();
            else
                return p;
        }

        Node() {    // Used to establish initial head or SHARED marker
        }

        Node(Thread thread, Node mode) {     // Used by addWaiter
            this.nextWaiter = mode;
            this.thread = thread;
        }

        Node(Thread thread, int waitStatus) { // Used by Condition
            this.waitStatus = waitStatus;
            this.thread = thread;
        }
    }

二.加锁

1.从lock进入锁

首先找到加锁方法的入口,源码第284行进入

    public void lock() {
        // sync分为公平和非公平
        sync.lock();
    }

对于加锁,ReentranLock可以实现公平锁和非公平锁,也是互斥锁,可重入锁。

2.非公平锁

源码第205行

        final void lock() {
            // 通过CAS的方式将state从0修改为1,如果返回true代表修改成功,否则修改失败
            if (compareAndSetState(0, 1))
                // 将一个属性设置为当前线程,这个属性是AQS的父类提供,证明当前线程拿到了锁资源
                setExclusiveOwnerThread(Thread.currentThread());
            else
                // 获取失败后执行的方法
                acquire(1);
        }

3.acquire方法

源码第1197行

    public final void acquire(int arg) {
        // 再次尝试获取锁资源,如果尝试成功直接返回true
        if (!tryAcquire(arg) &&
            // 获取锁失败后将当前线程追加为一个Node,追加到AQS的双向链表中
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            // 中断线程
            selfInterrupt();
    }

4.tryAcquire方法

源码第212行

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }

源码第129行

    final boolean nonfairTryAcquire(int acquires) {
            // 获取当前线程
            final Thread current = Thread.currentThread();
            // 获取AQS中的state值
            int c = getState();
            // 如果state为0,代表可以尝试获取锁资源
            if (c == 0) {
                // 尝试CAS修改state,如果成功返回ture,否则返回false
                if (compareAndSetState(0, acquires)) {
                    // 设置属性为当前线程
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            // 说明state不是0,判断当前占用锁资源的是否是当前线程,是则进行可重入操作
            else if (current == getExclusiveOwnerThread()) {
                // 将state+1
                int nextc = c + acquires;
                // 如果state+1后小于0,说明超过了锁可重入的最大值
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                // 没问题就对state重新赋值
                setState(nextc);
                // 锁重入成功
                return true;
            }
            return false;
        }

也就是说tryAcquire()返回true的情况为获取锁成功获取重入锁成功两种情况,如果没有获取锁成功,则进行acquireQueued方法的判断。

5.addWaiter

AQS源码第605行

    // 说明获取锁失败,放到等待队列中等待
    private Node addWaiter(Node mode) {
        // 创建Node类并绑定当前线程,设置为排他锁
        Node node = new Node(Thread.currentThread(), mode);
        // 获取AQS队列的尾部节点
        Node pred = tail;
        // 如果队列中有其他节点在排队
        if (pred != null) {
            node.prev = pred;
            // 基于CAS方式将当前节点设置为尾节点
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        // 走到这里说明没人排队或者前面CAS失败也会往队列尾部添加节点
        enq(node);
        return node;
    }

AQS第583行

    private Node enq(final Node node) {
        // 死循环
        for (;;) {
            // 获取当前节点的尾节点
            Node t = tail;
            // 如果尾节点为空说明没人其他节点排队
            if (t == null) {
                // 通过CAS操作将当前节点设为尾节点,初始化一个空节点作为头节点,这个头节点没有意义
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                // 有节点排队,往队列尾部添加节点
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

6.acquireQueued方法

AQS源码第857行

    // 已经将node加入到了双向队列中,然后执行当前方法
    final boolean acquireQueued(final Node node, int arg) {
        // 标识
        boolean failed = true;
        try {
            // 标识
            boolean interrupted = false;
            // 死循环
            for (;;) {
                // 返回当前node的前驱节点,如果获取不到抛空指针异常
                final Node p = node.predecessor();
                // 如果前驱节点p是头节点,说明此时队列只有当前node节点,尝试再次获取锁资源(state从0-1,或者锁重入)
                if (p == head && tryAcquire(arg)) {
                    // 走到这里说明获取锁成功,设置node节点为当前头节点,此时队列中无节点排队
                    setHead(node);
                    p.next = null; // help GC
                    // 将标识修改为false
                    failed = false;
                    return interrupted;
                }
                // 到这里说明没获取到锁资源,前驱节点是有效节点才会返回true,才会将线程阻塞,等待唤醒
                if (shouldParkAfterFailedAcquire(p, node) &&
                    // 基于UnSafe类的park()方法挂起线程,唯一可能出现异常的地方
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            // 执行的机率为0,除非JVM内部出现问题
            if (failed)
                cancelAcquire(node);
        }
    }

7. shouldParkAfterFailedAcquire方法

AQS源码第795行

    // pred是上一个节点,node是当前节点
    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        // 前驱节点的状态
        int ws = pred.waitStatus;
        // 如果上个节点状态为-1,则返回true
        if (ws == Node.SIGNAL)
            return true;
        // 如果当前节点失效,则找一个不失效的节点
        if (ws > 0) {
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            // 将重新标识好的上一个有效节点指向当前节点
            pred.next = node;
        } else {
            // 小于等于0但是不为-1,基于CAS操作则将前驱节点的状态改为-1
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

psvm_code

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值