ReentrantLock源码分析

核心结构

通过Sync静态内部类继承AQS实现锁的机制,两个子类FairSync 和 NonfairSync 分别对应公平和非公平锁。 有关AQS的原理参考juejin.cn/post/748158... ,本文涉及AQS的方法不再重复分析。

加锁

NonfairSync 非公平锁加锁

scss 复制代码
final void lock() {
    //CAS设置为1,成功加锁成功
    if (compareAndSetState(0, 1))
        //设置独占线程
        setExclusiveOwnerThread(Thread.currentThread());
    else
        //否则尝试获取锁
        //AQS中的方法
        acquire(1);
}

acquire(int arg)

scss 复制代码
public final void acquire(int arg) {
    if (
    //尝试获获取锁
    !tryAcquire(arg) &&
        //失败加入同步队列
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        //需要中断则中断
        selfInterrupt();
}
arduino 复制代码
protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}
java 复制代码
final boolean nonfairTryAcquire(int acquires) {
    //当前线程
    final Thread current = Thread.currentThread();
    //获取状态
    int c = getState();
    //状态为0说明之前获取锁的线程已经释放,可以去加锁操作了
    if (c == 0) {
        //将state设置为1,成功则加锁成功
        if (compareAndSetState(0, acquires)) {
            //设置当前线程独占标识
            setExclusiveOwnerThread(current);
            //返回加锁成功
            return true;
        }
    }
    //是独占线程又来获取锁了
    else if (current == getExclusiveOwnerThread()) {
        //状态为之前的状态值加1
        int nextc = c + acquires;
        //无效状态抛异常
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        //设置状态值    
        setState(nextc);
        //返回加锁成功
        return true;
    }
    //加锁失败
    return false;
}

非公平锁加锁逻辑总结

  1. 先尝试获取锁,如果加锁成功设置独占标识然后返回,失败则调用AQS中的方法尝试获取锁;
  2. 尝试获取锁,判断状态为0说明没有线程在执行同步代码块,可以去加锁,成功设置独占标识;判断是否是获取锁的线程再次去获取锁,是的话在之前的状态值加1(锁的重入)
  3. 获取锁失败加入同步队列,当前加锁节点的前驱节点是头节点再次尝试获取锁,否则自旋排队直到前驱节点是头节点。

FairSync 公平锁加锁

lock()

scss 复制代码
final void lock() {
    acquire(1);
}
scss 复制代码
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}
java 复制代码
    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {
            
            if (
            //判断当前线程前面是否有排队的线程
            //有咱就不插队去枪锁
            //这其实就是公平锁与非公平锁的区别,非公平锁不管前面有没排队的线程都去尝试获取锁,非公平锁发现前面有排队获取锁的线程,就老实也在后面跟着排队,到咱了再去获取锁
            !hasQueuedPredecessors() &&
                compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        else if (current == getExclusiveOwnerThread()) {
            int nextc = c + acquires;
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
    }
}
java 复制代码
public final boolean hasQueuedPredecessors() {
    Node t = tail; // Read fields in reverse initialization order
    Node h = head;
    Node s;
    return
    //h==t的话说明队列是空的或者队列仅有一个无效等待的节点
    //h!=t说明同步队列中存在有效节点在排队
    h != t &&
        //说明头节点已有值(end()方法入队时候pre已经设置了值,但tail还没来得及设置,这时候认为有节点在排队)
        ((s = h.next) == null || 
        //不等于当前线程,说明有其他节点在排队
        s.thread != Thread.currentThread());
}

公平锁加锁逻辑总结: 区别在于hasQueuedPredecessors()方法

解锁

release(int arg)

java 复制代码
public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            //唤醒后继线程
            unparkSuccessor(h);
        return true;
    }
    return false;
}
scss 复制代码
protected final boolean tryRelease(int releases) {
    //状态减1
    int c = getState() - releases;
    //必须要是加锁的线程去解锁
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    //状态是0说明释放锁成功了
    if (c == 0) {
        free = true;
        //清楚独占线程标识
        setExclusiveOwnerThread(null);
    }
    //设置state值
    setState(c);
    return free;
}

解锁逻辑总结: 公平锁和非公平锁解锁是相同的逻辑,把state值减为0,解锁成功。

Condition 操作

arduino 复制代码
final ConditionObject newCondition() {
    return new ConditionObject();
}
csharp 复制代码
//AQS中的
public ConditionObject() { }

对于ReentrantLock的condition 等待和唤醒机制是AQS中的代码,本文不再重复分析,简单总结下逻辑。

  • 等待await()。构建一个条件节点加入到条件队列,释放锁,阻塞park()线程直到唤醒signal()重新获取锁。
  • 唤醒signal()。从条件队列对头节点迁移到同步队列队尾并取消阻塞unpark()
相关推荐
Libby博仙21 分钟前
Spring Boot 条件化注解深度解析
java·spring boot·后端
源代码•宸39 分钟前
Golang原理剖析(Map 源码梳理)
经验分享·后端·算法·leetcode·golang·map
小周在成长42 分钟前
动态SQL与MyBatis动态SQL最佳实践
后端
瓦尔登湖懒羊羊1 小时前
TCP的自我介绍
后端
小周在成长1 小时前
MyBatis 动态SQL学习
后端
子非鱼9211 小时前
SpringBoot快速上手
java·spring boot·后端
我爱娃哈哈1 小时前
SpringBoot + XXL-JOB + Quartz:任务调度双引擎选型与高可用调度平台搭建
java·spring boot·后端
JavaGuide1 小时前
Maven 4 终于快来了,新特性很香!
后端·maven
开心就好20251 小时前
全面解析iOS应用代码混淆和加密加固方法与实践注意事项
后端
Thomas游戏开发1 小时前
分享一个好玩的:一次提示词让AI同时开发双引擎框架
前端·javascript·后端