源码解析 ReentrantLock

==基于 Java 1.8.0_91==

相关类的继承体系

这里写图片描述

1. 查看 ReentrantLock.lock() 源码

    public void lock() {
        //默认创建使用的是非公平同步器:NonfairSync
        //可以通过构造函数参数来设置公平同步器:FairSync
        sync.lock();
    }

查看 NonfairSync.lock()

    final void lock() {
            //如果当前同步状态为 0,通过原子操作设置为 1,表示加锁 1 次
            //return:true 表示无任何其他线程占用锁,获取锁成功
            if (compareAndSetState(0, 1))
                //设置当前线程为 “独占线程”
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

查看 AbstractQueuedSynchronizer 的 acquire 方法

    public final void acquire(int arg) {
        //1. tryAcquire 尝试获取锁,如果失败则进入下一步
        //2. acquireQueued 进入队列无限等待,直到获得锁,如果返回 true,进入下一步
        //3. 此时表示线程在等待的过程中被中断了,selfInterrupt 手动中断当前线程
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();

        //这里简单介绍一下 addWaiter 方法:
        //以排他/独占模式,将当前线程打包成一个节点
        //将该节点插入队列末尾,如果有必要,先无限循环直到队列初始化成功,再进行插入
        //return:返回该打包好的节点
    }

接下来分两个方向:tryAcquire 方法或 acquireQueued 方法

先查看 NonfairSync 的 tryAcquire 方法

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

查看 Sync 的 nonfairTryAcquire 方法

    final boolean nonfairTryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        //获取当前的同步状态
        int c = getState();
        //同步状态=0 表示无任何线程占有锁
        if (c == 0) {
            //如果当前同步状态为 0,通过原子操作设置为 1,表示加锁 1 次
            //return:true 表示无任何其他线程占用锁,获取锁成功
            if (compareAndSetState(0, acquires)) {
                //设置当前线程为 “独占线程”
                setExclusiveOwnerThread(current);
                //获取锁成功
                return true;
            }
        }
        //如果当前线程就是 “独占线程”,同步状态+1,在原有基础上再一次加锁(当占有锁的线程再次调用了 lock() 就会产生这种情况)
        else if (current == getExclusiveOwnerThread()) {
            //注意:同步状态有多少个 1,就要减去多少个 1,直至结果为 0 才表示锁(系统)资源不被占用
            //这就是为什么加锁多少次,就要相应地解锁多少次,才能成功释放锁
            int nextc = c + acquires;
            //整型内存溢出,类似于 byte 最大值为 127,再 +1 就变成 -128
            if (nextc < 0) // overflow
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        //获取锁失败,返回 false
        return false;
    }

如果获取锁成功,即返回 true,不再需要执行什么,直接返回最初方法调用处,代码继续往下执行


但如果获取锁失败,即返回 false,则进入 AbstractQueuedSynchronizer 的 acquireQueued 方法,参数是一个将当前线程打包好的独占模式的节点,并且已成功插入等待队列末尾

    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            //无限循环
            for (;;) {
                //获取当前节点的前继节点
                final Node p = node.predecessor();
                //如果前继节点为等待队列的 head,调用 tryAcquire,具体代码前面已注释分析
                if (p == head && tryAcquire(arg)) {
                    //到达此处,表示 tryAcquire 成功,即当前线程成功获取锁
                    //设置当前节点为等待队列的 head
                    setHead(node);
                    p.next = null; // help GC
                    //布尔值,设置整个流程成功
                    failed = false;
                    //注意:这里返回 interrupted
                    //true 表示:线程已中断,在 AbstractQueuedSynchronizer 的 acquire 方法处会手动中断当前线程
                    //false 表示:未中断线程,直接返回最初方法调用处,代码继续往下执行
                    return interrupted;
                }
                //到达此处,有两种可能原因:(1)前继节点并非 head;(2)尝试获取锁失败
                //shouldParkAfterFailedAcquire 判断是否应该挂起当前线程,如果返回 true,表示应该挂起
                //如果应该挂起,执行 parkAndCheckInterrupt,挂起当前线程,当被唤醒后需要检查线程中断标志,判断是否线程中断,是的话返回 true
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    //到这里表示线程已中断,设置布尔值使得在获得锁时返回 true,进而手动设置线程中断
                    interrupted = true;
            }
        } finally {
            //如果整个流程未成功,取消获取锁流程,参数为包裹当前线程的节点
            if (failed)
                cancelAcquire(node);
        }
    }

先看看 shouldParkAfterFailedAcquire 方法,了解一下判断是否应该挂起线程

    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        //查看前继节点的等待状态
        int ws = pred.waitStatus;

        if (ws == Node.SIGNAL)
            //表示前继节点的后继者(即当前节点)允许唤醒,可以挂起
            return true;
        if (ws > 0) {
            //表示前继节点被取消,将之前的连续几个被取消的前驱节点从队列中剔除,返回 false(即不挂起)
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            //如果是其他等待状态,尝试设置为 Node.SIGNAL,无论成功与否都不挂起
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

由上可知,只有在前继节点的等待状态为 Node.SIGNAL 时,才返回 true(表示挂起),并执行 parkAndCheckInterrupt 方法

    private final boolean parkAndCheckInterrupt() {
        //使用当前 AbstractQueuedSynchronizer 对象,挂起当前线程
        LockSupport.park(this);
        //被唤醒后,检查线程中断标志
        return Thread.interrupted();
    }

最后看一下 cancelAcquire 方法,看看其是如何取消获取锁的整个流程

    private void cancelAcquire(Node node) {
        // Ignore if node doesn't exist
        if (node == null)
            return;

        node.thread = null;

        // 剔除 “取消状态” 的所有前继节点
        Node pred = node.prev;
        while (pred.waitStatus > 0)
            node.prev = pred = pred.prev;

        Node predNext = pred.next;

        node.waitStatus = Node.CANCELLED;

        //如果是末尾节点,仅仅删除自身
        if (node == tail && compareAndSetTail(node, pred)) {
            compareAndSetNext(pred, predNext, null);
        } else {
            //到达此处,表示存在后继节点
            int ws;
            //如果前继节点有效,将当前节点的前后节点拼接起来
            if (pred != head &&
                ((ws = pred.waitStatus) == Node.SIGNAL ||
                 (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
                pred.thread != null) {
                //获取后继节点
                Node next = node.next;
                //设置前继节点的 next 指向后继节点
                if (next != null && next.waitStatus <= 0)
                    compareAndSetNext(pred, predNext, next);
            } else {
                //前继节点无效,直接唤醒当前节点的有效后继者(即 “非取消状态” 的后继节点)
                unparkSuccessor(node);
            }

            node.next = node; // help GC
        }
    }

查看 unparkSuccessor 方法(该方法能唤醒当前节点的一个后继节点,将前面代码介绍的线程挂起状态给解除)

    private void unparkSuccessor(Node node) {
        /*
         * If status is negative (i.e., possibly needing signal) try
         * to clear in anticipation of signalling.  It is OK if this
         * fails or if status is changed by waiting thread.
         */
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);

        //获取需要被唤醒的一个后继节点
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            //后继节点无效,从后往前遍历,找到位于前方的第一个有效节点
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
            //唤醒该节点
            LockSupport.unpark(s.thread);
    }

2. 查看 ReentrantLock.unlock() 源码

    public void unlock() {
        sync.release(1);
    }

查看 AbstractQueuedSynchronizer 的 release 方法

    public final boolean release(int arg) {
        //尝试释放锁
        if (tryRelease(arg)) {
            //释放锁成功,获取等待队列的 head
            Node h = head;
            //如果 head 有效,唤醒 head 的有效后继者(即 “非取消状态” 的后继节点)
            //可以简单理解为:队列先进先出,锁(系统)资源分配给下一个线程节点
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        //释放锁失败,有可能是解锁次数不足,也有可能是真的失败了
        return false;
    }

查看 Sync 的 tryRelease 方法

    protected final boolean tryRelease(int releases) {
        //将同步状态-1(如果同步状态=3,表示由同一线程加锁 3 次)
        int c = getState() - releases;
        //如果当前线程不是 “独占线程”,抛出错误
        //举个例子:如果一个线程明明没有获得锁,却尝试释放锁,就会抛错
        if (Thread.currentThread() != getExclusiveOwnerThread())
            throw new IllegalMonitorStateException();
        //表示是否应该释放锁,因为有时候同一个锁可能被加锁多次,一次释放是不够的
        boolean free = false;
        //同步状态=0,表示无任何线程占有锁,锁(系统)资源是自由的
        if (c == 0) {
            free = true;
            setExclusiveOwnerThread(null);
        }
        setState(c);
        return free;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值