ReentrantLock源码分析笔记-单线程公平锁为例

2023-12-25 17:38:29 浏览数 (2)

前提

1)大致了解AQS原理(☆☆☆)

2)熟悉双向链表的基本操作

3)本文以公平锁上锁释放锁为例跟ReentrantLock代码(☆☆☆)

4)本文以单线程抢占锁释放锁为例(☆☆☆)

5)建议了解公平锁和非公平锁的区别

6)较好的理解能力(作者表达能力差)

AQS

AQS的核心思想(参考版)

如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并将共享资源设置为锁定状态,如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。 CLH(Craig,Landin,and Hagersten)队列是一个虚拟的双向队列,虚拟的双向队列即不存在队列实例,仅存在节点之间的关联关系。 AQS是将每一条请求共享资源的线程封装成一个CLH锁队列的一个结点(Node),来实现锁的分配。

用大白话来说,AQS就是基于CLH队列,用volatile修饰共享变量state,线程通过CAS去改变状态符,成功则获取锁成功,失败则进入等待队列,等待被唤醒。

**注意:AQS是自旋锁:**在等待唤醒的时候,经常会使用自旋(while(!cas()))的方式,不停地尝试获取锁,直到被其他线程获取成功

实现了AQS的锁有:自旋锁、互斥锁、读锁写锁、条件产量、信号量、栅栏都是AQS的衍生物

AQS的核心思想(作者版)

大致的思想是:

当一个线程发出上锁的请求时,会看state状态,

如果是0(没有上锁),则上锁(state=1),处理业务逻辑;

如果不是0(别的线程占有锁),则把当前线程封装成一个Node节点插入双向链表中等待。

当上锁的线程释放锁后会唤醒双向链表中的第一个节点中的线程继续使用锁。

当然中间有很多细节,需要自己悟仔细悟,我只想说 Doug Lea NB.

代码语言:javascript复制
public abstract class AbstractQueuedSynchronizer
        extends AbstractOwnableSynchronizer
        implements java.io.Serializable {

    /*当前持有锁的线程,AbstractOwnableSynchronizer里的属性*/
    private transient Thread exclusiveOwnerThread;
 
    /*双向链表的头指针*/
    private transient volatile Node head;
 
    /*双向链表的尾指针*/
    private transient volatile Node tail;
 
 
    /*锁的状态,1表示上锁,0表示没有锁,大于1表示可重入锁*/
    private volatile int state;
 
    /*双向链表的Node节点*/
    static final class Node {
 
        /*每一个请求上锁的线程都会封装成一个双向链表*/
        volatile Thread thread;
        /*双向链表的prev指针*/
        volatile Node prev;
        /*双向链表的next指针*/
        volatile Node next;
        
 
    }
}

测试代码:单线程公平锁为例

图解
上锁

main函数

代码语言:javascript复制
//一个线程上锁释放锁,没有任何并发情况
public class Start {
    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock(true);
        System.out.println("--lock-");
        lock.lock();
        System.out.println("---logic--");
        lock.unlock();
        System.out.println("--unlock--");
 
    }
}

跟lock.lock()代码(公平锁),就跟到下面代码

代码语言:javascript复制
//FairSync 
final void lock() {
            //参数1很重要
            acquire(1);
}

继续跟

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

下面开始执行 tryAcquire(arg) 方法

代码语言:javascript复制
//FairSync
protected final boolean tryAcquire(int acquires) {//acquires=1
            //获取当前线程
            final Thread current = Thread.currentThread();
            //获取lock的状态(0表示没有上锁,1表示上锁,大于1表示重入锁)
            int c = getState();//c=0,因为c是int类型,初始化为0
            if (c == 0) {//走这个分支
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
               //不走这个分支
               //不走这个分支
               //不走这个分支
            }
            return false;
        }

跟hasQueuedPredecessors()方法(这个方法贼难,贼难,贼难)

代码语言:javascript复制
//AbstractQueuedSynchronizer
public final boolean hasQueuedPredecessors() {
        //获取双向链表的头结点和尾结点,此时都为null
        Node t = tail; 
        Node h = head;
        Node s;
        //h != t  返回 false,后面就不用执行了,整体返回false
        return h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
    }

tryAcquire(arg)里的hasQueuedPredecessors()返回false, !hasQueuedPredecessors() 返回true, 所以继续跟compareAndSetState(0, acquires)方法

代码语言:javascript复制
//AbstractQueuedSynchronizer
protected final boolean compareAndSetState(int expect, int update) { //expect=0,update=1
        //stateOffset = unsafe.objectFieldOffset
                (AbstractQueuedSynchronizer.class.getDeclaredField("state"));
        //修改stateOffset就是修改state
        // 本文的逻辑是在单线程情况下,所以cas会成功操作
        //即 return  true
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }

此时tryAcquire方法里!hasQueuedPredecessors() &&compareAndSetState(0, acquires) 整体是真,将执行setExclusiveOwnerThread(current)方法

代码语言:javascript复制
//AbstractOwnableSynchronizer
protected final void setExclusiveOwnerThread(Thread thread) {
        //上面通过cas成功设置锁的状态,然后这里将使用锁的线程保存下来
        exclusiveOwnerThread = thread;
    }

acquire里的tryAcquire返回true, !tryAcquire 返回false, 即acquire执行完毕

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

------------------------------------------------>此时就是上锁成功 (最显著的结果就是将state从0变为1,并且在一个变量AbstractOwnableSynchronizer中的exclusiveOwnerThread属性保存了拥有锁的线程)

释放锁

lock.unlock()跟到下面代码

代码语言:javascript复制
//ReentrantLock
public void unlock() {
        sync.release(1);
    }

继续跟进release方法

代码语言:javascript复制
//AbstractQueuedSynchronizer     
public final boolean release(int arg) {//GAG=1
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

跟tryRelease方法

代码语言:javascript复制
//Sync
protected final boolean tryRelease(int releases) {
            //将state减少releases,此处state=1,releases=1
            int c = getState() - releases;
            //如果释放锁的线程不是当前线程,肯定不合适,谁上锁谁释放锁
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            //如果c==0,说明锁释的状态为空闲,将当前上锁的线程设置为空
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            //修改新的state状态
            setState(c);
            return free;
        }

------------------------------------------------>此时就是释放锁成功 (最显著的结果就是将state从1变为0,并且在一个变量AbstractOwnableSynchronizer中的exclusiveOwnerThread属性设置为null)

参考

JUC AQS ReentrantLock源码分析(一)_java_lyvee的专栏-CSDN博客_reentrantlock源码

https://ke.qq.com/course/455581?taid=4036264236348317

AbstractQueuedSynchronizer源码解读 - 活在夢裡 - 博客园

AQS详解(面试)_mulinsen77的博客-CSDN博客_aqs

AQS框架源码分析_Niulx-CSDN博客_aqs源码

https://segmentfault.com/a/1190000015768003 https://blog.csdn.net/fxkcsdn/article/details/82217760

0 人点赞