并发编程--ReentrantLock

ReentrantLock概念

     

 AQS队列同步器是锁的底层实现:最上层中:state:锁的状态 acquire()加锁 release()解锁

  

        ReentrantLock是Java中的一个可重入的独占锁(互斥锁),它属于JDK级别的锁实现,与JVM级别的synchronized关键字在功能上有相似之处,但提供了更多的灵活性和控制能力。

        比synchronized更加灵活,能自己设置什么时候定义锁,什么时候释放锁,ReentrantLock 提供了显式的 lock() 和 unlock() 方法,允许程序员在代码中明确指定何时获取和释放锁。

ReentrantLock 和 synchronized 都是可重入锁

ReentrantLock 又分为公平锁和非公平锁,默认使用非公平锁

一、基本概念

  • 独占锁(互斥锁):每次只能有一个线程能持有锁,其他线程必须等待锁被释放后才能获取。
  • 可重入性:ReentrantLock支持可重入性,即同一个线程在持有锁的情况下,可以多次获取该锁而不会导致死锁。每次成功获取锁后,锁的持有计数会增加;每次释放锁时,计数会减少,直到计数为0时锁才真正被释放。

ReentrantLock的公平锁和非公平锁

  • 公平锁:按照线程在队列中的排队顺序,先到者先拿到锁

  • 非公平锁:当线程要获取锁时,先通过两次 CAS 操作去抢锁,如果没抢到,当前线程加入到队列中等待唤醒。

ReentrantLock 默认采用非公平锁,因为考虑获得更好的性能,通过 boolean 来决定是否用公平锁(传入 true 用公平锁)。

下面介绍写ReentrantLock 的公平锁

static final class FairSync extends Sync {
    final void lock() {
        acquire(1);
    }
    // AbstractQueuedSynchronizer.acquire(int arg)
    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();
        int c = getState();
        //判断当前是否加锁
        if (c == 0) {
            // 1. 和非公平锁相比,这里多了一个判断:是否有线程在等待
            //判断是否是第一个等待的,同时cas尝试加锁
            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;
    }
}

核心逻辑就是:

        1.首先根据根state判断是否加锁,没加锁进2,加锁了判断是否是当前线程加锁,是当前线程,那么state的值继续+1也就是重入锁(递归,或者同一线程下加锁方法对另一个加锁方法的调用)

        2.判断是否是等待的第一个同时用cas尝试加锁,都为true时,对该方法加锁

下面介绍写ReentrantLock 的非公平锁

static final class NonfairSync extends Sync {
    final void lock() {
        // 2. 和公平锁相比,这里会直接先进行一次CAS,成功就返回了
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1);
    }
    // AbstractQueuedSynchronizer.acquire(int arg)
    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}
/**
 * Performs non-fair tryLock.  tryAcquire is implemented in
 * subclasses, but both need nonfair try for trylock method.
 */
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;
}

核心逻辑就是:

        1.首先什么都不管,先进行一次CAS抢锁,失败进入2

        2.首先根据根state判断是否加锁,没加锁进2,加锁了判断是否是当前线程加锁,是当前线程,那么state的值继续+1也就是重入锁(递归,或者同一线程下加锁方法对另一个加锁方法的调用)

        2.判断不用判断是否是等待的第一个,直接用cas尝试加锁

总结:公平锁和非公平锁只有两处不同:

  1. 非公平锁在调用 lock 后,首先就会调用 CAS 进行一次抢锁,如果这个时候恰巧锁没有被占用,那么直接就获取到锁返回了。

  2. 非公平锁在 CAS 失败后,和公平锁一样都会进入到 tryAcquire 方法,在 tryAcquire 方法中,如果发现锁这个时候被释放了(state == 0),非公平锁会直接 CAS 抢锁,但是公平锁会判断等待队列是否有线程处于等待状态,如果有则不去抢锁,乖乖排到后面。

        公平锁和非公平锁就这两点区别,如果这两次 CAS 都不成功,那么后面非公平锁和公平锁是一样的,都要进入到阻塞队列等待唤醒。

        相对来说,非公平锁会有更好的性能,因为它的吞吐量比较大。当然,非公平锁让获取锁的时间变得更加不确定,可能会导致在阻塞队列中的线程长期处于饥饿状态。

  • 19
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值