ReentrantLock 之lock() unLock()源码分析(基于非公平锁)

本文深入剖析ReentrantLock的lock()和unlock()方法源码,详细解读Node节点属性及功能,阐述非公平锁与公平锁的区别。在lock()方法中,通过CAS操作尝试获取锁,失败则进入等待队列;unlock()方法则释放锁并唤醒等待队列中的线程。公平锁在尝试获取锁时会检查是否有线程在等待,而非公平锁则不作此检查,增加了竞争性。
摘要由CSDN通过智能技术生成

Node需要了解的属性和它的功能

waitStatus这个字段解释: 这个字段其实是表示它的next节点的一个状态(初始化默认0,其他取值范围有CANCELLED、SIGNAL、CONDITION,意义见下)

-AbstractQueuedSynchronizer
    -Node
        SHARED = new Node() //表示时共享锁的
        EXCLUSIVE = null //表示时独占的
        CANCELLED = 1 //被取消的节点
        SIGNAL = -1 //表示Node对象 在等待队列里
        CONDITION = -2 //表示Node对象 在条件队列里
        PROPAGATE //
        waitStatus //该节点现在的状态 取值范围CANCELLED、SIGNAL、CONDITION
        prev //指向前一个Node节点
        next //指向下一个Node节点
        thread //当前的线程
        nextWaiter //条件队列里下一个等待节点

lock() 详解

//sync类有2个实现类,公平和非公平锁
//加锁入口
public void lock() {
    sync.lock();
}
//这是非公平锁实现的sync.lock();
final void lock() {
    //compareAndSetState(0, 1) Unsafe类直接操作内存改变AQS类中的state字段的值,修改成功则表示加锁成功
    if (compareAndSetState(0, 1))
    	//加锁成功后将当前线程放入到AQS类继承AbstractOwnableSynchronizer类中exclusiveOwnerThread字段
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1);
}
//再次尝试去加锁acquire(1);
public final void acquire(int arg) {
    //tryAcquire(arg) 再次尝试加锁,这个尝试方法由公平和非公平锁自己实现
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))  //Node.EXCLUSIVE这个值再独占锁中是NULL
        selfInterrupt();//如果进了这里,表示该线程已结束了,或者请求取消了
}
//tryAcquire(arg) 接第18行代代码,参数acquires的值为1
protected final boolean tryAcquire(int acquires) {
	return nonfairTryAcquire(acquires);
}
//接第24行代码,参数acquires的值为1;该方法功能是,尝试加锁和判断是否是重入锁的操作
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    //如果为0,表示锁是释放状态
    if (c == 0) {
        //尝试再次加锁
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    //表示当前锁已被占用,这里的if是判断当前线程和占有锁的线程是否是同一个线程
    else if (current == getExclusiveOwnerThread()) {
        //将状态加1,这是可重入锁,锁lock锁是需要加几次锁就要释放几次锁
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    //尝试抢锁失败
    return false;
}
//目光回到第19行代码addWaiter(Node.EXCLUSIVE)方法
//上面抢锁失败,方法作用:将该线程封装,加入等待队列。参数mode为独占锁默认的NULL值
private Node addWaiter(Node mode) {
    //创建有一个Node节点,放入当前线程,mode这个参数在独占锁必为NULL,这个构造中将mode赋值给了nextWait,就是指向下一个等待节点
    Node node = new Node(Thread.currentThread(), mode);
    // 获取当前AQS类中的位节点
    Node pred = tail;
    //如果尾节点不为空,表示该等待队列已经建立好了
    if (pred != null) {
        //将当前新Node对象的前指针指向当前AQS类中尾节点
        node.prev = pred;
        //compareAndSetTail(pred, node)  CAS方式将之前的尾节点变成当前新创建的Node节点
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            return node;
        }
    }
    //走到这里,表示还未创建等待队列
    enq(node);
    return node;
}
//接68行,方法作用:初始化等待队列。参数node为当前线程的Node对象
private Node enq(final Node node) {
    //自旋创建等待队列的头结点
    for (;;) {
        //获取当前AQS的尾节点
        Node t = tail;
        if (t == null) { 
        	// CAS方式创建AQS中head节点,就是队列的头结点
            if (compareAndSetHead(new Node()))
            //并且将尾节点的指针也指向头结点
                tail = head;
        } else {
            //表示将包含当前线程的Node对象中的前指针指向79行new出来的头节点
            node.prev = t;
            //尝试将AQS的尾指针换成当前线程的Node对象,这里换不成功,会一直自旋,直到换成功为止
            if (compareAndSetTail(t, node)) {
                t.next = node;//将上一个node对象的下节点指向新创建的node
                return t;
            }
        }
    }
}
//接19行,作用:尝试申请入队列。参数node为当前线程的封装类,arg为1
//这个方法时lock()最终执行方法,线程没获取到锁,就会在这个方法里面挂起来
final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) { //当该线程为获取到锁时,就会阻塞在118行那个方法,当其他线程释放锁后且该线程为head的下有一个Node节点,那么这个自旋又会开始
            //获取当前线程node对象的前节点
            final Node p = node.predecessor();
            //判断当前node对象是否是head的next节点,如果是就会再次去抢一下锁
            if (p == head && tryAcquire(arg)) {
                //抢锁成功后,将head设置为当前线程的node,setHead中会将前节点和Thread置为null
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            //抢锁不成功或者前节点不为head节点时
            /*
             * shouldParkAfterFailedAcquire(p, node)新node节点调用此方法,第一次必定返回false,所以
             * 必定还会走一次for循环,这次for循环又给了这个Node对象去抢锁的机会,所以新Node节点会有2此重复抢锁的机会;
             *第二次for循环时shouldParkAfterFailedAcquire(p, node)返回值就是true了,接下来执行parkAndCheckInterrupt()方法;
             *
             */
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}
//接117行,作用:查看当前线程的前节点中的waitStatus的状态是否为-1(-1表示next节点为等待队列的节点,-2表示next节点为条件队列的节点)
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    int ws = pred.waitStatus;
    //新创建的Node对象第一次进来时,它的头节点必定为0
    if (ws == Node.SIGNAL)//这个值是Node.SIGNAL = -1
        return true;
    if (ws > 0) {
        
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
         //不为-1时,就将当前Node的前节点设置为 -1
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    //第一次进来时,返回值为false,
    return false;
}
//接118行,作用:将该线程设置成等待线程,调用了java源语Unsafe的park方法
private final boolean parkAndCheckInterrupt() {
    LockSupport.park(this);
    return Thread.interrupted();
}

unLock()源码分析

public void unlock() {
    //为什么是传1,因为unLock操作只会释放一次加锁,所以前面说的调用了几次lock方法,就需要同样调几次unLock方法
    sync.release(1);
}
//子类都是调用的AQS父类的release方法
public final boolean release(int arg) {
    //这个tryRelease方法是ReentrantLock自己实现的
    if (tryRelease(arg)) {
        Node h = head;//获取当前的head节点
        if (h != null && h.waitStatus != 0)//判断head的下一个节点是否需要被唤醒,这里waitStatus永远表示的都是next节点的状态,参照Node类的定义
            unparkSuccessor(h);//准备去唤醒线程
        return true;
    }
    return false;
}
//接8行,tryRelease方法是ReentrantLock自己实现的。尝试释放锁
protected final boolean tryRelease(int releases) {
    int c = getState() - releases;//从22行判断可以看出,c必须==0才能成功释放锁
    if (Thread.currentThread() != getExclusiveOwnerThread())//判断释放锁的线程是不是当前占有锁的线程
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);//将当前占有锁的线程清空
    }
    setState(c);//将锁状态置为0
    return free;
}
//接11行,AQS类的方法,方法作用:主要是去唤醒一个线程;参数node表示当前等待队列的head节点
private void unparkSuccessor(Node node) {
    int ws = node.waitStatus;
    if (ws < 0)
    //每次有线程释放锁的时候,都会去把头结点的waitStatus设置为0;当有线程去加锁的时候,会判断这个waitStatus是否为-1,不为-1时,抢锁的线程就会自旋设置该值为-1
        compareAndSetWaitStatus(node, ws, 0);

    Node s = node.next;//该线程释放锁后,准备去唤醒head节点的next节点
    //如果head节点没有next节点了,或者next节点的waitStatus>0(表示该线程已经被取消了)
    if (s == null || s.waitStatus > 0) {
        s = null;
        //这里是从尾指针往前找,找一个waitStatus <= 0的线程,并且是离头部最近的线程,将这个线程拿去作为唤醒的对象
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
                s = t;
    }
    //如果36行代码的s不为空,那么久会去尝试唤醒next节点,这里唤醒后,就会去执行上面讲解的lock()源码的99行代码的自旋方法(线程是被阻塞在lock源码讲解的118行那个方法)
    if (s != null)
        LockSupport.unpark(s.thread);
}

公平锁与非公平锁之间的区别

非公平锁tryAcquire的源码

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源码

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;
        }

它俩的区别就在于公平锁中的第5行的那个方法

//返回true则表示该线程前面还有线程在等待,就不会去抢锁
public final boolean hasQueuedPredecessors() {
        Node t = tail; // Read fields in reverse initialization order
        Node h = head;
        Node s;
        return h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IT界的老菜鸟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值