精华内容
下载资源
问答
  • AQS公平锁和非公平锁(二) 一、概念 注意:因为ReentrantLock 类可以实现公平锁和非公平锁,所以本文的讲解以该类为主。 1.1 公平锁 ​ 多个线程按照申请锁的顺序去获得锁,线程会直接进入队列去排队,永远都是...

    AQS之公平锁和非公平锁(二)

    一、概念

    注意:因为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(0, 1))
                    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时才会竞争锁;

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

    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现成的方法。具体参考AQS之理论知识(一)对此的描述。

    2.4 signal

    ​ 因为signal方法最终调用的是Condition中的方法,所以AQS中signal方法无法重写,都是公用AQS现成的方法。具体参考AQS之理论知识(一)对此的描述。

    三、总结

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

    ​ 3.2公平锁和非公平锁的在ReentrantLock实现上的区别:

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

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

    展开全文
  • 基于AQS(比如ReentrantLock)原理大体是这样:有一个state变量,初始值为0,假设当前线程为A,每当A获取一次,status++. 释放一次,status--.会记录当前持有的线程。当A线程拥有的时候,status>0. B线程...

    基于AQS的锁(比如ReentrantLock)原理大体是这样:
    有一个state变量,初始值为0,假设当前线程为A,每当A获取一次锁,status++. 释放一次,status--.锁会记录当前持有的线程。
    当A线程拥有锁的时候,status>0. B线程尝试获取锁的时候会对这个status有一个CAS(0,1)的操作,尝试几次失败后就挂起线程,进入一个等待队列。
    如果A线程恰好释放,--status==0, A线程会去唤醒等待队列中第一个线程,即刚刚进入等待队列的B线程,B线程被唤醒之后回去检查这个status的值,尝试CAS(0,1),而如果这时恰好C线程也尝试去争抢这把锁

    非公平锁实现:
    C直接尝试对这个status CAS(0,1)操作,并成功改变了status的值,B线程获取锁失败,再次挂起,这就是非公平锁,B在C之前尝试获取锁,而最终是C抢到了锁。
    公平锁:
    C发现有线程在等待队列,直接将自己进入等待队列并挂起,B获取锁

    转载于:https://www.cnblogs.com/chengdabelief/p/7493200.html

    展开全文
  • 公平锁: 当hasQueuedPredecessors 返回false时,则表示不需要去排队,会直接进行cas操作去修改state状态 hasQueuedPredecessors解析 当hasQueuedPredecessors返回false: ht相等:一.当队列未初始化时头节点...

    场景:一个线程运行完毕,准备去等待队列中唤醒(unpark)线程。
    公平锁:

    在这里插入图片描述
    在这里插入图片描述

    当hasQueuedPredecessors 返回false时,则表示不需要去排队,会直接进行cas操作去修改state状态
    hasQueuedPredecessors解析
    当hasQueuedPredecessors返回false:
    h和t相等:一.当队列未初始化时头节点和尾节点都为null,固然相等;二.当队列已经初始化并且已经有线程产生过队列,并且线程已经释放了state状态置为0,则头节点和尾节点都指向同一个节点,即傀儡节点,这个节点是没有线程属性的。
    h和t不相等,(s = h.next) == null 和s.thread != Thread.currentThread() 都为false,整个方法才能放回false
    当头节点和尾节点不同时,说明队列肯定有>=2数量的节点,则头节点的next节点肯定不等于null,则第一个条件返回了false。
    当前线程和next节点的线程相等时返回false,说明当前线程占有了锁,无需排队,方法直接放回false,直接通过compareAndSetState修改state状态。

    当hasQueuedPredecessors返回true:
    h和t不相等,表示队列中有>=2数量的节点。
    h.next为null时,说明刚好有线程在操作队列把头节点的next节点脱离队列,则表示已经该线程正在修改state,所以需要排队,或者next.thread不为当前线程时说明已经有线程修改完state,把node节点的线程置为自己的线程,则当前线程也需要去排队。

    非公平锁:

    在这里插入图片描述

    在这里插入图片描述

    当非公平锁调用lock方法时,一开始就去尝试修改state,如果成功则直接占有状态,并且设置当前线程,不管是刚刚进入的直接去拿锁或者后面实现的tryAcquire的方法都没有检查是否需要排队的操作,所以这是非公平锁和公平锁最主要的区别,当线程释放锁成功,公平锁会去检查自己是否要去排队,只要其他线程比当前线程快或者当前有等待队列,则直接去排队;非公平锁则不一样,线程会去尝试拿锁,不管当前有没有正在等待的队列,一旦拿锁成功,表示插队成功,非公平的意义体现了。如果拿锁失败,并且当前有队列,则该线程会插入队尾。插入队列以后,队列中的线程顺序都是固定无法改变的。

    展开全文
  • AQS中的公平锁和非公平锁

    千次阅读 2020-03-29 08:06:18
    正文 公平锁:多个线程按照申请锁的顺序去获得锁,线程会直接进入队列去排队,永远都是...非公平锁:多个线程去获取锁的时候,会直接去尝试获取,获取不到,再去进入等待队列,如果能获取到,就直接获取到锁。 ...

    正文

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

    • 优点:所有的线程都能得到资源,不会饿死在队列中。

    • 缺点:吞吐量会下降很多,队列里面除了第一个线程,其他的线程都会阻塞,cpu唤醒阻塞线程的开销会很大。

    非公平锁:多个线程去获取锁的时候,会直接去尝试获取,获取不到,再去进入等待队列,如果能获取到,就直接获取到锁。

    • 优点:可以减少CPU唤醒线程的开销,整体的吞吐效率会高点,CPU也不必取唤醒所有线程,会减少唤起线程的数量。

    • 缺点:你们可能也发现了,这样可能导致队列中间的线程一直获取不到锁或者长时间获取不到锁,导致饿死。

    我举个例子给他家通俗易懂的讲一下的,想了好几天终于在前天跟三歪去肯德基买早餐排队的时候发现了怎么举例了。

    现在是早餐时间,敖丙想去kfc搞个早餐,发现有很多人了,一过去没多想,就乖乖到队尾排队,这样大家都觉得很公平,先到先得,所以这是公平锁咯。

    那非公平锁就是,敖丙过去买早餐,发现大家都在排队,但是敖丙这个人有点渣的,就是喜欢插队,那他就直接怼到第一位那去,后面的鸡蛋,米豆都不行,我插队也不敢说什么,只能默默忍受了。

    但是偶尔,鸡蛋也会崛起,叫我滚到后面排队,我也是欺软怕硬,默默到后面排队,就插队失败了。

    其实在大家经常使用的ReentrantLock中就有相关公平锁,非公平锁的实现了。

    大家还记得我在乐观锁、悲观锁章节提到的Sync类么,是ReentrantLock他本身的一个内部类,他继承了AbstractQueuedSynchronizer,我们在操作锁的大部分操作,都是Sync本身去实现的。

     

    Sync呢又分别有两个子类:FairSync和NofairSync

     

    他们子类的名字就可以见名知意了,公平和不公平那又是怎么在代码层面体现的呢?

    公平锁:

     

    你可以看到,他加了一个hasQueuedPredecessors的判断,那他判断里面有些什么玩意呢?

     

    代码的大概意思也是判断当前的线程是不是位于同步队列的首位,是就是返回true,否就返回false。

    我总觉得写到这里就应该差不多了,但是我坐下来,静静的思考之后发现,还是差了点什么。

    上次聊过ReentrantLock了,但是AQS什么的我都只是提了一嘴,一个线程进来,他整个处理链路到底是怎样的呢?

    公平锁到底公平不公平呢?让我们一起跟着丙丙走进ReentrantLock的内心世界。

    上面提了这么多,我想你应该是有所了解了,那一个线程进来ReentrantLock这个渣男是怎么不公平的呢?(默认是非公平锁)

    我先画个图,帮助大家了解下细节:

    ReentrantLock的Sync继承了AbstractQueuedSynchronizer也就是我们常说的AQS

     

     

    他也是ReentrantLock加锁释放锁的核心,大致的内容我之前一期提到了,我就不过多赘述了,他们看看一次加锁的过程吧。

    A线程准备进去获取锁,首先判断了一下state状态,发现是0,所以可以CAS成功,并且修改了当前持有锁的线程为自己。

     

    这个时候B线程也过来了,也是一上来先去判断了一下state状态,发现是1,那就CAS失败了,真晦气,只能乖乖去等待队列,等着唤醒了,先去睡一觉吧。

     

    A持有久了,也有点腻了,准备释放掉锁,给别的仔一个机会,所以改了state状态,抹掉了持有锁线程的痕迹,准备去叫醒B。

     

    这个时候有个带绿帽子的仔C过来了,发现state怎么是0啊,果断CAS修改为1,还修改了当前持有锁的线程为自己。

    B线程被A叫醒准备去获取锁,发现state居然是1,CAS就失败了,只能失落的继续回去等待队列,路线还不忘骂A渣男,怎么骗自己,欺骗我的感情。

     

    诺以上就是一个非公平锁的线程,这样的情况就有可能像B这样的线程长时间无法得到资源,优点就是可能有的线程减少了等待时间,提高了利用率。

    现在都是默认非公平了,想要公平就得给构造器传值true。

    ReentrantLock lock = new ReentrantLock(true);
     

     

    说完非公平,那我也说一下公平的过程吧:

    线A现在想要获得锁,先去判断下state,发现也是0,去看了看队列,自己居然是第一位,果断修改了持有线程为自己。

     

    线程b过来了,去判断一下state,嗯哼?居然是state=1,那cas就失败了呀,所以只能乖乖去排队了。

     

    线程A暖男来了,持有没多久就释放了,改掉了所有的状态就去唤醒线程B了,这个时候线程C进来了,但是他先判断了下state发现是0,以为有戏,然后去看了看队列,发现前面有人了,作为新时代的良好市民,果断排队去了。

     

    线程B得到A的召唤,去判断state了,发现值为0,自己也是队列的第一位,那很香呀,可以得到了。

     

    总结:

    总结我不说话了,但是去获取锁判断的源码,箭头所指的位置,现在是不是都被我合理的解释了,当前线程,state,是否是0,是否是当前线程等等,都去思考下。

     

     

    展开全文
  • 最近在读AQS的源码,想一步一步分析记录下来,接下来阅读源码,看一看ReentrantLock公平锁和非公平锁的实现。 public ReentrantLock() { sync = new NonfairSync(); } public ReentrantLock(boolean fair) {...
  • 之前笔者解析了AQS的源码,在JUC中有很多是基于AQS实现的,今天想写个简单的ReentrantLock实现,代码也基本是在看了ReentrantLock源码后写出来的,做个笔记
  • 公平锁和非公平锁只有两处不同 非公平锁在调用 lock 后,首先就会调用 CAS 进行一次抢锁,如果这个时候恰巧锁没有被占用,那么直接就获取到锁返回了。 非公平锁在 CAS 失败后,和公平锁一样都会进入到 ...
  • 公平锁和非公平锁 ReentrantLock 默认采用非公平锁,除非你在构造方法中传入参数 true 。 public ReentrantLock() { sync = new NonfairSync(); } public ReentrantLock(boolean fair) { sync = fair ? new ...
  • PS:该文章是借鉴掘金的 石衫的架构笔记 附上借鉴的所有链接: 大白话聊聊Java并发面试问题之Java 8如何优化CAS性能?...谈到公平锁和非公平锁,首先要引入2个概念。一个是CAS,一个是AQS。 CAS:全名叫做C...
  • ReentrantLock这篇文章是从JDK8的ReentrantLock源码角度来分析ReentrantLock是怎么利用AQS来实现公平锁,和非公平锁的。所以前提需要理解AQS。 /** * A reentrant mutual exclusion {@link Lock} with the same ...
  • 公平锁和非公平锁 Condition 1. 将节点加入到条件队列 2. 完全释放独占锁 3. 等待进入阻塞队列 4. signal 唤醒线程,转移到阻塞队列 5. 唤醒后检查中断状态 6. 获取独占锁 7. 处理中断状态 * 带超时机制的 ...
  • 大家推荐个靠谱的公众号程序员探索之路,公众号内点击网赚获取彩蛋,大家一起加油,这个公众号已经接入图灵​ 前言:看这篇文章之前... 1.ReentrantLock 的公平锁和非公平锁 代码上的区别 非公平锁: 这里直接...
  • 前言 jdk1.8中的AQS(AbstractQuenedSynchronizer基类)提供了封装了做...所有公平和非公平的核心逻辑在tryAcquire和tryRelease两个方法中,本文结合源码和加锁解锁流程图分析公平锁和非公平锁的优缺点,关联关...
  • 真的理解公平锁和非公平锁嘛?

    千次阅读 2019-03-26 18:43:35
    之前一直对公平锁和非公平锁的概念不是特别清楚,简单理解为只要当前资源被加锁,之后的请求都会搁置到队列中公平锁就是严格按照FIFO(先进先出)的规矩办事。非公平锁就是不遵守这个先进先出的规矩,恶性竞争资源。在...
  • 对于ReentrantLock,一般我们在调用无参构造函数的时候,构造的是非公平锁,当前类也存在一种可以指定锁类型...我们今天主要讨论下这两个内部类,从而探讨下代码级别是如何实现公平锁和非公平锁的。 先看Reentra...
  • ReentrantLock 提供了公平锁和非公平锁,只需要在构造方法中使用一个 boolean 参数即可。默认非公平锁。 今天从源码层面看看区别和具体实现。 1. 类 UML 图 ReentrantLock 内部有一个抽象类 Sync,继承了 ...
  • ReentrantLock 提供了公平锁和非公平锁,只需要在构造方法中使用一个 boolean 参数即可。默认非公平锁。 今天从源码层面看看区别和具体实现。 1. 类 UML 图 ReentrantLock 内部有一个抽象类 Sync,继承了 AQS。 而...
  • 公平锁与非公平锁

    2021-04-19 16:22:31
    ReentrantLock和synchronized的自JDK1.6以来主要的区别...但是以上八股文的背法,为了更好的理解公平锁与非公平锁,笔者简单的模拟了公平锁和非公平锁的竞争的情况,详细的注释已经在代码里了。 package JUC;​impo
  • ReentrantLock内部是通过AQS实现锁的功能,有公平锁和非公平锁两种实现。 公平锁,即锁的获取顺序按线程申请锁的先后顺序。 非公平锁,当一个线程t1申请锁时,锁刚好释放。即使已有其他线程在t1之前申请锁排队,...
  • Java中的synchronized实现原理1.6版本做了什么优化对象头升级机制偏向轻量级...注意,synchronized关键字修饰类静态方法,住类对象;修饰方法,住调用此方法的对象 实现原理 synchronized是jvm实现的一种互
  • 文章目录AQSAQS底层使用了模板方法模式ReentrantLock锁的实现分析公平锁和非公平锁公平锁FairSync非公平锁NonfairSyncReentrantLock等待队列中元素的唤醒线程安全性问题总结 AQS AQS原理 AQS:...
  • 可以看到这个过程跟AQS独占锁的获取释放是一样的,实现公平锁的关键是tryRelease方法的实现: 公平锁实现关键: 当有资源时也会判断当前有没有线程在等待,只要有线程在等待不管有没有资源都要排队等待,这就保证...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 453
精华内容 181
关键字:

aqs公平锁和非公平锁