JUC之ReentrantLock源码分析

ReentrantLock有两种锁:NonfairSync(非公平锁),FairSync(公平锁),本文基于NonfairSync分析ReentrantLock的源码。

使用示例

static ReentrantLock lock = new ReentrantLock();

static void testReentrantLock() {
    lock.lock();
    try {
        System.out.println("同步代码块");
    } finally {
        lock.unlock();
    }
}

public static void main(String[] args) {
    new Thread(ReentrantLockDemo::testReentrantLock, "Thread-01").start();
    new Thread(ReentrantLockDemo::testReentrantLock, "Thread-02").start();
}

线程Thread-01和Thread-02只有一个能进入同步代码块,另一个线程阻塞在lock.lock()的位置,只有进入同步代码块的线程执行lock.unlock()之后,阻塞线程才能进入同步代码块。

 源码分析

获取锁原理分析

通过CAS操作,获取共享变量(state)的控制权;如果获取成功(state:0->1),该线程即可执行同步代码块的内容,否则就要进入AQS队列,等待获取锁的线程释放锁(state:1->0)后竞争对共享变量的控制。

public void lock() {
    sync.lock();
}
final void lock() {
    // 不关心AQS队列是否存在线程排队,直接进行CAS操作,尝试获取共享变量的控制,获取成功就说明获取锁成功
    // CAS操作是线程安全的,可以保证只有一个线程能执行成功
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
        // CAS操作失败,需要进行后续操作
        acquire(1);
}
public final void acquire(int arg) {
    // tryAcquire:
    // 			  1、再一次进行CAS操作,尝试获取共享变量的控制
    //			  2、判断是否为重入锁,如果是重入锁,直接获取共享变量的控制
    // addWaiter(Node.EXCLUSIVE):将当前线程添加到AQS队列(Node.EXCLUSIVE:表示为互斥锁)
    // acquireQueued:自旋,等待获取获取锁
    if (!tryAcquire:(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}
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()) {
        // 锁重入,直接更新state值即可
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}
private Node addWaiter(Node mode) {
    // 初始化一个Node节点,mode此时为null,只要知道该标识表名为互斥锁即可
    Node node = new Node(Thread.currentThread(), mode);
    // tail指向AQS的尾部节点
    Node pred = tail;
    // AQS队列不为空
    if (pred != null) {
        // 将新增节点的前置节点设置为AQS的尾部节点
        node.prev = pred;
        // 将tail指向新增的节点
        if (compareAndSetTail(pred, node)) {
            // 将AQS的尾部节点的后置节点设置为新增的节点
            pred.next = node;
            return node;
        }
    }
    // AQS队列为空,执行此处操作
    enq(node);
    return node;
}
private Node enq(final Node node) {
    // 自旋,直到新增节点插入到AQS队列
    for (;;) {
        // tail指向AQS的尾部节点
        Node t = tail;
        // AQS队列为空,需要初始化AQS队列
        if (t == null) { 
            // 由于此处线程不安全,通过CAS操作,保证只有一个线程能执行初始化操作
            // head指向新增的头部节点(该节点没有存储任务信息,只是表示一个开始节点)
            if (compareAndSetHead(new Node()))、
                // tail也指向新增的头部节点
                tail = head;
        } else {
            // 此时说明AQS队列已经初始化成功
            // 将当前节点的前置节点设置为AQS队列的尾部节点
            node.prev = t;
            // 由于此处不是线程安全的,通过CAS操作,保证只有一个线程能够将tail引用指向新增的节点,其他线程等待下一次循环,重新添加到AQS队列的尾部
            if (compareAndSetTail(t, node)) {
                // 将AQS的尾部节点的后置节点设置为新增的节点
                t.next = node;
                return t;
            }
        }
    }
}

AQS队列结构图 

/**
* 自旋,如果需要挂起时,就将当前线程挂起,等待唤醒
*/
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)) {
                // 将当前节点设置为head节点
                setHead(node);
                // 原head节点等待垃圾回收
                p.next = null; 
                failed = false;
                return interrupted;
            }
            // 尝试获取锁失败后,判断是否需要将线程挂起
            // shouldParkAfterFailedAcquire:判断当前线程是否需要挂起(ws表示前置节点的状态)
            // 								ws = -1:表示当前线程需要被挂起
            // 								ws > 0:前置节点线程被cancel,此时需要移出AQS队列中所有被取消的线程
            //								ws为其他:通过CAS操作更新前置节点的状态为-1,等待下次循环再次判断是否需要被挂起
            // parkAndCheckInterrupt:线程挂起,等待进入代码块的线程执行unlock操作后根据AQS入队顺序依次唤醒
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    // 前置节点状态
    int ws = pred.waitStatus;
    // -1 表示当前线程需要被挂起
    if (ws == Node.SIGNAL)
        return true;
    // 0 表示前置节点被cancel,此时遍历AQS队列当前节点前面所有的节点,清理所有被cancel的节点
    if (ws > 0) {
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
        // 更新前置节点为-1
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}

private final boolean parkAndCheckInterrupt() {
    // 线程挂起,让出CPU
    LockSupport.park(this);
    // 线程被唤醒,获取在挂起的过程中,当前线程是否被中断的标识
    return Thread.interrupted();
}

释放锁原理分析

执行一次unlock方法,state值减1,当state=0时,获取AQS的head节点,并唤醒head的节点的后置节点所属的线程,并尝试获取共享变量的控制。

public void unlock() {
    // 公平锁和非公平锁的释放原理一致,实现逻辑由AbstractQueuedSynchronizer类实现
    sync.release(1);
}
// tryRelease:state值减1,如果state最终结果为0,说明锁完全释放成功,唤醒AQS队列中的第一个节点
if (tryRelease(arg)) {
    // 获取AQS队列的head节点
    Node h = head;
    // h !=null 说明AQS队列不为空
    // h.waitStatus != 0 说明AQS队列存在需要被唤醒的线程(阻塞线程进入AQS队列时,会将前置节点的waitStatus值设置为-1)
    if (h != null && h.waitStatus != 0)
        unparkSuccessor(h);
    return true;
}
return false;

protected final boolean tryRelease(int releases) {
    // 共享变量释放releases个单位
    int c = getState() - releases;
    // 如果当前线程不是获取锁的线程,说明存在其他线程非法调用释放锁的操作,直接抛出异常
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    // free主要用来标识当前线程是否完全释放对锁的控制(重入锁会导致state值增加,每重入一次,state就加1)
    boolean free = false;
    if (c == 0) {
        // 释放共享变量
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}

private void unparkSuccessor(Node node) {
 
    // 前置节点waitStatus值设置为默认值
    int ws = node.waitStatus;
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);

    // 获取需要唤醒的节点
    Node s = node.next;
    // s == null 说明没有需要唤醒的线程
    // s.waitStatus > 0 说明需要唤醒的线程已经被cancel了,此时从AQS队列的尾部往前遍历,获取最后一个需要被唤醒的线程节点
    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);
}

AQS的第一个节点被唤醒。此时原有的head节点等待垃圾回收,第一个阻塞线程节点变为head节点,并获取了共享变量的控制,进入同步代码块。 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值