ReentrantLock 原理及源码剖析

首先看看ReentrantLock 的UML类图

ReentrantLock 的UML类图

1. 非公平锁实现原理

1.1 加锁解锁流程

从构造器看,ReentrantLock 默认实现为非公平锁

public ReentrantLock() {
    sync = new NonfairSync();
}

NonfairSync 继承自 AQS

  • 没有竞争时

没有竞争时

static final class NonfairSync extends Sync {

    final void lock() {
    	//没有竞争时,将锁的状态从0改为1,成功则设置锁的owner为当前线程,失败则
    	//进入else块
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1);
    }

    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}
  • 第一个竞争出现时

第一个竞争出现时
进入acquire(1)

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        //下面的语句的意思是创建一个节点对象加入到队列里面去
        //addWaiter流程:
        //1.如果tail不为空,则将node设置为tail并返回node
        //2.如果tail为空,则初始化tail,同上将node设置为tail并返回node
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

===========================addWaiter代码如下===========================
private Node addWaiter(Node mode) {
    Node node = new Node(Thread.currentThread(), mode);
   
    Node pred = tail;
    if (pred != null) {
        node.prev = pred;
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            return node;
        }
    }
    enq(node);
    return node;
}
=====================================================================
private Node enq(final Node node) {
    for (;;) {
        Node t = tail;
        if (t == null) { // Must initialize
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
            node.prev = t;
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
}
  • Thread-1 执行了
  1. CAS 尝试将 state 由 0 改为 1,结果失败
  2. 进入 tryAcquire 逻辑,这时 state 已经是1,结果仍然失败
  3. 接下来进入 addWaiter 逻辑,构造 Node 队列
    • 图中黄色三角表示该 Node 的 waitStatus 状态,其中 0 为默认正常状态
    • Node 的创建是懒惰的
    • 其中第一个 Node 称为 Dummy(哑元)或哨兵,用来占位,并不关联线程
      在这里插入图片描述
  • 当前线程进入 acquireQueued 逻辑
  1. acquireQueued 会在一个死循环中不断尝试获得锁,失败后进入 park 阻塞
  2. 如果自己是紧邻着 head(排第二位),那么再次 tryAcquire 尝试获取锁,当然这时 state 仍为 1,失败
  3. 进入 shouldParkAfterFailedAcquire 逻辑,将前驱 node,即 head 的 waitStatus 改为 -1(-1的意思是,该节点由责任去唤醒下一个节点),这次返回 false

在这里插入图片描述

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            //获取前驱节点
            final Node p = node.predecessor();
            //如果前驱节点为head意味着该节点处于第二位,还有机会获得锁
            if (p == head && tryAcquire(arg)) {
                //当获取锁成功后,设置当前node为head
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            //shouldParkAfterFailedAcquire()意思是获取锁失败后是否要阻塞
            //如果该方法返回真,则调用parkAndCheckInterrupt()阻塞住
            //返回假的话则继续做下一轮的循环
            if (shouldParkAfterFailedAcquire(p, node) &&
                //当锁释放开后,该线程被unpark唤醒,然后设置interrupted为
                //true,并继续执行循环
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}
  1. shouldParkAfterFailedAcquire 执行完毕回到 acquireQueued ,再次 tryAcquire 尝试获取锁,当然这时state 仍为 1,失败
  2. 当再次进入 shouldParkAfterFailedAcquire 时,这时因为其前驱 node 的 waitStatus 已经是 -1,这次返回true
  3. 进入 parkAndCheckInterrupt, Thread-1 park(灰色表示)
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    int ws = pred.waitStatus;
    //前驱节点状态已经是-1
    if (ws == Node.SIGNAL)
        return true;
    if (ws > 0) {
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}
============================================================
private final boolean parkAndCheckInterrupt() {
    LockSupport.park(this);
    return Thread.interrupted();
}
  • 再次有多个线程经历上述过程竞争失败,变成这个样子
    在这里插入图片描述
  • Thread-0 释放锁,进入 tryRelease 流程,如果成功
  1. 设置 exclusiveOwnerThread 为 null
  2. state = 0
    在这里插入图片描述
  • 当前队列不为 null,并且 head 的 waitStatus = -1,进入 unparkSuccessor 流程,
    找到队列中离 head 最近的一个 Node(没取消的),unpark 恢复其运行,本例中即为 Thread-1
    回到 Thread-1 的 acquireQueued 流程
    在这里插入图片描述
public void unlock() {
    sync.release(1);
}
=============================================================
public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        //head不为空且状态不为0(即-1)
        if (h != null && h.waitStatus != 0)
            //唤醒后继节点(离head最近的一个没取消的node,unpark恢复其运行)
            unparkSuccessor(h);
        return true;
    }
    return false;
}
=============================================================
protected final boolean tryRelease(int releases) {
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) {
        free = true;
        //设置owner为null
        setExclusiveOwnerThread(null);
    }
    //设置状态为0
    setState(c);
    return free;
}
=============================================================
private void unparkSuccessor(Node node) {

    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);
}
  • 如果加锁成功(没有竞争),会设置
  1. exclusiveOwnerThread 为 Thread-1,state = 1
  2. head 指向刚刚 Thread-1 所在的 Node,该 Node 清空 Thread
  3. 原本的 head 因为从链表断开,而可被垃圾回收
  • 如果这时候有其它线程来竞争(非公平的体现),例如这时有 Thread-4 来了
    在这里插入图片描述
  • 如果不巧又被 Thread-4 占了先
  1. Thread-4 被设置为 exclusiveOwnerThread,state = 1
  2. Thread-1 再次进入 acquireQueued 流程,获取锁失败,重新进入 park 阻塞

1.2 可重入原理

static final class NonfairSync extends Sync {

    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()) {
        // state++
            int nextc = c + acquires;
            if (nextc < 0) // overflow
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
    }
===============================================

    protected final boolean tryRelease(int releases) {
        // state--
        int c = getState() - releases;
        if (Thread.currentThread() != getExclusiveOwnerThread())
            throw new IllegalMonitorStateException();
        boolean free = false;
        // 支持锁重入, 只有 state 减为 0, 才释放成功
        if (c == 0) {
            free = true;
            setExclusiveOwnerThread(null);
        }
        setState(c);
        return free;
    }
}   

1.3 可打断模式

  • 不可打断模式

在此模式下,即使它被打断,仍会驻留在 AQS 队列中,一直要等到获得锁后方能得知自己被打断了

// Sync 继承自 AQS
static final class NonfairSync extends Sync {
    // ...
    private final boolean parkAndCheckInterrupt() {
        // 如果打断标记已经是 true, 则 park 会失效
        LockSupport.park(this);
        // interrupted 会清除打断标记
        return Thread.interrupted();
    }
    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null;
                    failed = false;
                    // 还是需要获得锁后, 才能返回打断状态
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt()) {
                    // 如果是因为 interrupt 被唤醒, 返回打断状态为 true
                    interrupted = true;
                }
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) {
            // 如果打断状态为 true
            selfInterrupt();
        }
    }
    static void selfInterrupt() {
        // 重新产生一次中断
        Thread.currentThread().interrupt();
    }
}
  • 可打断模式

static final class NonfairSync extends Sync {
    public final void acquireInterruptibly(int arg) throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        // 如果没有获得到锁, 进入 ㈠
        if (!tryAcquire(arg))
            doAcquireInterruptibly(arg);
    }
    // ㈠ 可打断的获取锁流程
    private void doAcquireInterruptibly(int arg) throws InterruptedException {
        final Node node = addWaiter(Node.EXCLUSIVE);
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt()) {
                    // 在 park 过程中如果被 interrupt 会进入此
                    // 这时候抛出异常, 而不会再次进入循环
                    throw new InterruptedException();
                }
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
}

2.公平锁实现原理

// 与非公平锁主要区别在于 tryAcquire 方法的实现
protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        // 先检查 AQS 队列中是否有前驱节点, 没有才去竞争
        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;
}

3.条件变量实现原理

  • 3.1 await 流程

public final void await() throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    //加入condition队列中    
    Node node = addConditionWaiter();
    //释放锁
    int savedState = fullyRelease(node);
    int interruptMode = 0;
    while (!isOnSyncQueue(node)) {
        //将该线程park住
        LockSupport.park(this);
        if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
            break;
    }
    if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
        interruptMode = REINTERRUPT;
    if (node.nextWaiter != null) // clean up if cancelled
        unlinkCancelledWaiters();
    if (interruptMode != 0)
        reportInterruptAfterWait(interruptMode);
}
  1. 开始 Thread-0 持有锁,调用 await,进入 ConditionObject 的 addConditionWaiter 流程
    创建新的 Node 状态为 -2(Node.CONDITION),关联 Thread-0,加入等待队列尾部
    在这里插入图片描述
private Node addConditionWaiter() {
    Node t = lastWaiter;
    // If lastWaiter is cancelled, clean out.
    if (t != null && t.waitStatus != Node.CONDITION) {
        unlinkCancelledWaiters();
        t = lastWaiter;
    }
    Node node = new Node(Thread.currentThread(), Node.CONDITION);
    if (t == null)
        firstWaiter = node;
    else
        t.nextWaiter = node;
    lastWaiter = node;
    return node;
}
  1. 接下来进入 AQS 的 fullyRelease 流程,释放同步器上的锁
    在这里插入图片描述
final int fullyRelease(Node node) {
    boolean failed = true;
    try {
        int savedState = getState();
        if (release(savedState)) {
            failed = false;
            return savedState;
        } else {
            throw new IllegalMonitorStateException();
        }
    } finally {
        if (failed)
            node.waitStatus = Node.CANCELLED;
    }
}
  1. unpark AQS 队列中的下一个节点,竞争锁,假设没有其他竞争线程,那么 Thread-1 竞争成功在这里插入图片描述
  • 3.2 signal 流程

public final void signal() {
    //首先检查调用该方法的线程是不是锁的持有者
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    Node first = firstWaiter;
    if (first != null)
        doSignal(first);
}
  1. 假设 Thread-1 要来唤醒 Thread-0
    在这里插入图片描述

  2. 进入 ConditionObject 的 doSignal 流程,取得等待队列中第一个 Node,即 Thread-0 所在 Node

private void doSignal(Node first) {
    do {
        //如果firstWaiter的下一个节点为null,则说明该队列只有一个node
        //并使lastWaiter也设置为null
        if ( (firstWaiter = first.nextWaiter) == null)
            lastWaiter = null;
        first.nextWaiter = null;
    } while (
    //将该节点加入到aqs尾部(等待队列),成功返回true,且不继续向下执行
    !transferForSignal(first) &&
             //失败后将寻找下一个节点,若下一个节点存在则唤醒下一个节点
             (first = firstWaiter) != null);
}

在这里插入图片描述

  1. 执行 transferForSignal 流程,将该 Node 加入 AQS 队列尾部,将 Thread-0 的 waitStatus 改为 0,Thread-3 的waitStatus 改为 -1
final boolean transferForSignal(Node node) {
    // 将节点的状态由-2改成0(因为在aqs队列里最后一个元素的状态是0)
    if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
        return false;
     //插入的队列后返回该节点的前驱节点
    Node p = enq(node);
    int ws = p.waitStatus;
    //当前驱节点的状态大于0时,表示该节点被取消
    //或当设置前驱节点状态失败时,则unpark该线程,否则返回true
    if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
        LockSupport.unpark(node.thread);
    return true;
}

在这里插入图片描述
7. Thread-1 释放锁,进入 unlock 流程

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值