前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JUC ReentrantLock 源码解析

JUC ReentrantLock 源码解析

作者头像
用户9347382
发布2022-01-10 16:47:25
2080
发布2022-01-10 16:47:25
举报
文章被收录于专栏:程序员阿杰

JUC

解析可重入锁的核心就是理解同步器 1.同步器(aqs):什么是同步器、同步器是一种数据结构、是同步队列的头节点、同步器中提供了大量的模板方法

代码语言:javascript
复制
 public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

以acquire方法为例子、同步器中并没有实现tryAcquire的方法、

代码语言:javascript
复制
 protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
    }

具体的实现需要通过子类重写tryAcquire方法来实现、接下来介绍addWaiter、acquireQueued的方法

addWaiter解析:

代码语言:javascript
复制
private Node addWaiter(Node mode) {
    Node node = new Node(mode);

    for (;;) {
        Node oldTail = tail;
        if (oldTail != null) {
            node.setPrevRelaxed(oldTail);
            if (compareAndSetTail(oldTail, node)) {
                oldTail.next = node;
                return node;
            }
        } else {
            initializeSyncQueue();
        }
    }
}

可以看出在当前线程tryAcquire尝试获取锁失败时、执行addWaiter的方法会通过当前线程构造同步节点 主要的方法compareAndSetTail会通过乐观锁的方式为构造出的node节点以尾插法插入同步队列中、之后返回设置好的节点加入acquireQueued中

代码语言:javascript
复制
final boolean acquireQueued(final Node node, int arg) {
    boolean interrupted = false;
    try {
        for (;;) {
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                return interrupted;
            }
            if (shouldParkAfterFailedAcquire(p, node))
                interrupted |= parkAndCheckInterrupt();
        }
    } catch (Throwable t) {
        cancelAcquire(node);
        if (interrupted)
            selfInterrupt();
        throw t;
    }
}

可以看出先会判断传入的节点是否拥有前驱节点、如果前驱节点与同步器中的节点也就是头节点相等、之后会去再次调用tryAcquire方法、尝试去加锁、如果加锁失败就是不断自旋、直到获取了锁 如果加锁成功、会设置头节点为传入的构造节点、并且抛出头节点进入gc环节(真是太残忍了、用完就扔掉)、返回布尔值 以上就是同步器的流程了 现在进入可重入锁的源码、之前我们说过acquire是模板方法、需要子类去实现tryAcquire方法、可以看出可重入锁是怎么实现的、由于可重入锁是默认的不公平锁、

代码语言:javascript
复制
public ReentrantLock() {
        sync = new NonfairSync();
}

static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;
    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}

final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

由以上代码可以看出、tryAcquire是通过设置同步器的state也就是状态完成的、当c=0时会通过乐观锁去设置state的状态、0是无线程进入锁、设置好锁之后、会设置线程暂存区域、方便后续、重入锁的设置中的线程判断、防止其他线程获取锁、当同一线程获取锁之后、state也会加1、以上就完成了可重入锁的所有流程。

本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名,转载请标明出处 最后编辑时间为: 2021/11/22 14:56

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-11-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档