波吉学源码——独占锁ReentrantLock源码剖析

ReentrantLock是可重入的独占锁,同时只能有一个线程可以获取该锁,其他获取该锁的线程会被阻塞而放入该锁的AQS阻塞队列里面

在这里插入图片描述

ReentrantLock由AQS来实现,并且根据参数来决定其内部是公平锁还是非公平锁,默认是非公平锁

其中Sync直接继承自AQS,它的子类分别实现了公平锁和非公平锁

AQS的state状态值表示线程获取该锁的可重入次数,在默认情况下,state的值为0表示当前锁没有任何线程持有,当一个线程第一次获取该锁时会尝试使用CAS设置state值为1,如果CAS成功则当前线程获取了该锁,然后记录该锁的持有者为当前线程,在该线程没有释放锁的情况下第二次获取该锁后,状态值被设置为2,这就是可重入次数,在该线程释放锁的情况时,会尝试使用CAS让状态值减1,如果减1后状态值为0,则当前线程释放该锁

获取锁

当一个线程调用该方法时,说明该线程希望获取该锁,如果锁当前没有被其他线程占有并且当前线程之前没有获取过该锁,则当前线程获取该锁,然后设置当前锁的拥有者为当前线程,设置AQS为1,直接返回。如果当前线程之前已经获取了该锁,则这次只是简单的把AQS的状态加一后返回。如果该锁已经被其他线程持有,则调用该方法后线程会被放入AQS阻塞队列中

public void lock() {
    sync.lock();
}

非公平锁

static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;

    final void lock() {
        //1.cas操作设置state从0到1
        if (compareAndSetState(0, 1))
            //2.首次获取锁:cas成功将锁被当前线程持有
            setExclusiveOwnerThread(Thread.currentThread());
        else
            //2.cas失败则调用aqs中的acquire()操作
            acquire(1);
    }
}

//AQS代码其中tryAcquire(arg)需要具体子类实现,是模板方法的体现
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        //如果是其他线程获取锁那么该线程会被放入阻塞队列
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

//调用nonfairTryAcquire(acquires)是ReentrantLock具体实现AQS中的tryAcquire
protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}

//非公平锁
final boolean nonfairTryAcquire(int acquires) {
    //1.获取当前线程
    final Thread current = Thread.currentThread();
    //2.获取state变量
    int c = getState();
    //3.判断state是否等于0
    if (c == 0) {
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    //4.如果是当前线程的持有者则将state+1
    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;
}

假设线程A因为获取不是线程持有者的锁而放入AQS阻塞队列中,B也尝试获取锁但是在B获取锁的时候状态值变为0了,那么B就有可能获取到锁,可是是A先获取的锁啊,这就体现了非公平,B获取锁采用了抢夺策略,没有查看AQS中是否有比自己请求锁更早的线程

公平锁

final void lock() {
    acquire(1);
}

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

//公平锁
protected final boolean tryAcquire(int acquires) {
    //获取当前线程
    final Thread current = Thread.currentThread();
    //获取状态state
    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;
}

//公平策略
public final boolean hasQueuedPredecessors() {
    //获取队列尾部
    Node t = tail;
    //获取队列头部
    Node h = head;
    Node s;
    //如果队头和队尾相等说明当前队列为空
    //如果队列不为空并且头节点的下一个节点为空说明将要有一个元素添加到队列中(AQS队列的插入)
    
    //队头和队尾不相等 && (头节点的next节点不为null || 头结点的next节点的线程不是当前线程)
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}

释放锁

//AQS中的release方法
public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

//ReentrantLock实现的方法
protected final boolean tryRelease(int releases) {
    //当前state状态减releases
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    //设置标志位
    boolean free = false;
    //如果c==0就释放锁
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

存量美团骑手

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

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

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

打赏作者

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

抵扣说明:

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

余额充值