AQS中公平锁和非公平锁区别,你知道么

>>强大,10k+点赞的 SpringBoot 后台管理系统竟然出了详细教程!

点击关注公众号,实用技术文章及时了解AQS中公平锁和非公平锁区别,你知道么

来源:blog.csdn.net/weixin_43823391/

article/details/114259418

一、概念

注意:因为ReentrantLock 类可以实现公平锁和非公平锁,所以本文的讲解以该类为主。

1.1 公平锁

多个线程按照申请锁的顺序去获得锁,线程会直接进入队列去排队,永远都是队列的第一位才能得到锁。

1.2 非公平锁

多个线程去获取锁的时候,会直接去尝试获取,获取不到,再去进入等待队列,如果能获取到,就直接获取到锁。在这个过程中,会和队列中的线程竞争,得到锁的顺序并不一定是先到先得。

1.3 ReentrantLock 类

ReentrantLock是一个可重入且独占式的锁,并且同时实现了公平锁和非公平锁,所以本文的讲解以该类为主。

1.3.1 Sync

Sync是RenntrantLock类的一个内部抽象类,官方解释为是一个帮助类。该类继承了AQS,所以具备AQS的大部分属性和方法,同时又重写了一些必要方法:tryRelease(),isHeldExclusively(),也就是说继承该类的类都具有该方法。

同时该类有新加一些自己的方法,供具体的实现类使用:nonfairTryAcquire()newCondition()nonfairTryAcquire()是为了非公平锁模式下竞争锁,newCondition(),最终是为了创建条件队列。

1.3.2 NonfairSync

NonfairSync继承了Sync,是一个实现类,重要的方法有:lock(),tryAcquire(int acquires)

1.3.3 FairSync

FairSync继承了Sync,是一个实现类,重要的方法有:lock(),tryAcquire(int acquires)

总结:公平锁和非公平锁在概念上的区分:是否优先进入队列。优先进入队列的线程是公平锁,优先抢占资源的是非公平锁。

二、公平锁和非公平锁重要方法

2.1 lock

使用RenntrantLock构造函数可以构建公平锁和非公平锁,两者区别主要体现在lock方法中,具体有两处,构造方法如下:

//默认非公平锁
public ReentrantLock() {
        sync = new NonfairSync();
    }
//传入true,可以构建公平锁
public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

ReentrantLock的获取锁方法的路径:lock()->acquire(),其中acquire()方法可以参考AQS之理论知识(一)中的讲解,两者的区别是lock方法以及不同实现类重写的tryAcquire()

2.2.1 NonfairSync 的lock
final void lock() {
   //区别一:非公平锁竞争锁资源会先去竞争锁,而公平锁只会在锁状态为0时才会竞争锁
            if (compareAndSetState(01))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

调用NonfairSync 的lock,直接竞争锁,成功的话直接返回。失败的话,执行acquire(),因为AQS是模块方法,所以NonfairSync 需要重写tryAcquire(),在这个方法中当锁状态为0时直接竞争锁,无需查看队列是否有线程。

2.2.2 FairSync的lock
final void lock() {
            acquire(1);
        }
 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;
        }

调用FairSync的lock,直接执行acquire(),因为AQS是模块方法,所以FairSync需要重写tryAcquire(),在这个方法中当锁状态为0时直接竞争锁,需要查看队列是否有线程,有线程的话将新线程添加到队列中,其他业务和非公平锁一致。

总结:区别一:非公平锁竞争锁资源会先去竞争锁,而公平锁只会在锁状态为0时才会竞争锁;

区别二:公平锁查看队列里面是否有节点,有的话,加入队列,没有的话直接区抢锁;而非公平锁则是直接抢锁。学习资料:Java进阶视频资源

2.2 unlock

public void unlock() {
        sync.release(1);
    }
    
    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

RenntrantLock的解锁调用的是AQS的release(),而解锁调用的tryRelease(arg)是两者共用的。所以解锁的步骤依然是两大步骤:

2.2.1 更改锁状态,如果锁被完全释放,同时将独占锁绑定的线程清空;
2.2.2 唤醒队列的节点绑定的线程。

2.3 await

因为await方法最终调用的是Condition中的方法,所以AQS中await方法无法重写,都是公用AQS现成的方法。

2.4 signal

因为signal方法最终调用的是Condition中的方法,所以AQS中signal方法无法重写,都是公用AQS现成的方法。

三、总结

3.1 公平锁和非公平锁在概念上的区分:是否优先进入队列。优先进入队列的线程是公平锁,优先抢占资源的是非公平锁;
3.2公平锁和非公平锁的在ReentrantLock实现上的区别:

区别一:非公平锁竞争锁资源会先去竞争锁,而公平锁只会在锁状态为0时才会竞争锁;

区别二:公平锁查看队列里面是否有节点,有的话,加入队列,没有的话直接区抢锁;而非公平锁则是直接抢锁。

推荐:

主流Java进阶技术(学习资料分享)

AQS中公平锁和非公平锁区别,你知道么
PS:因为公众号平台更改了推送规则,如果不想错过内容,记得读完点一下“在看”,加个“星标”,这样每次新文章推送才会第一时间出现在你的订阅列表里。“在看”支持我们吧!

原文始发于微信公众号(Java知音):AQS中公平锁和非公平锁区别,你知道么