《面试无忧》--AQS实现原理和源码分析

AQS同步器(AQS:AbstractQueuedSynchronizer)(JUC的核心类):

1.AQS主要解决什么问题?
多个线程访问共享数据,产生线程不安全的问题,模拟生活重场景,摆地摊(最近比较火),很多人同时购买商品,老板不知道谁付了钱,谁没付钱。我们把商品看作共享数据,每一个顾客就是一个线程,这样的话,老板就乱套了。怎么解决这个问题呢?
老板后面设置了二维码支付,让大家进行排队,一个一个的进行排队支付,排在最前面的顾客支付完,后面一个接着付钱。
当然在之前,我们都知道用sychronized关键字加锁可以实现,只有当线程占有了这个锁的时候,才能就行操作共享资源,操作完成后释放锁,其他线程再去竞争锁,拿到锁以后再进行操作数据,这样的操作虽然能保证原子性,但是它是串行化执行,极度影响性能。所以在java1.6以后,java大神Doug Lea(膜拜)开发了J.U.C的包,实现了Lock的接口,解决了很多并发编程的问题,优化了很多关于性能的问题,当然Lock最中心的思想就是我们的AQS,而AQS是利用CAS操作去修改数据(不知道CAS的童鞋可以持续关注,后续更新)。

2.抛出问题,需求分析
2.1那么问题来了,怎么能实现我们的要求呢?第一点,解决操作的互斥性,就是保证一个线程在操作的时候,其他线程是不能允许操作的;第二点,那么其他这么多的线程我们怎么去把它们存起来?用什么数据结构存?第三点,线程操作完以后怎么释放资源?怎么去唤醒下一个线程?
第一个问题:我们可以设置一个共享变量state,当然这个state也要保证原子性,当线程抢占了锁,就让state加1,释放了锁,就让state-1。当state为0的时候,线程就抢占锁,当state不为0的时候,就需要把线程加到队列中排队。
第二个问题:如果要把线程存起来,肯定需要一个队列(queue),满足先进先出(FIFO)的策略。可以使用队列或者链表结构,当线程没有抢占到锁的时候,就放到队列中,将其阻塞(运行中的线程是非常消耗性能)。
第三个问题:阻塞和唤醒操作,阻塞和唤醒的方式进行多,比如wait/notity、Thread.sleep()、codition、locksupport。wait/notity不能唤醒指定的线程,所以不行。休眠只能指定时间去唤醒线程,也不行。condition本身就是AQS所以不行(先有蛋还是先有鸡的问题),而locksupport本身就是c++操作线程实现阻塞和唤醒,来解决这类型的问题。所以locksupport最合适。

3.怎么去实现?
直接上图,我们分析ReentrantLock重入锁的方式剖析AQS的实现原理(只分析非公平锁的部分源码来解释原理,知道原理,才能更好的理解)。

首先说下ReentrantLock的结构,Sync是ReentrantLock的一个抽象内部类,它继承了AQS同步器。

它有两个子类NonfairSync和FairSync,分别实现了公平锁和非公平锁两种方式。
在这里插入图片描述

我们只分析非公平锁的源码,先用cas操作将状态由0变更成1,如果成功的话,将当前线程设置为运行的线程。如果没有成功说明当前状态不是0,而是1就进行acquire操作。
在这里插入图片描述
线程如果没有修改成功,就进行下面这个方法,第一步会执行tryAcquire()方法再尝试一次。
在这里插入图片描述
Node是AQS维护的一个双向链表结构,先去尾节点,如果有线程,就绑定前后关系,如果队列中是空,将队列头和尾都设置成当前线程。
在这里插入图片描述
tryAcquire()方法调用到这里,会再次尝试修改状态,如果修改成功,就设置当前线程为运行线程。如果当前状态不是0,当时也是当前线程,就将线程加1。为什么会这样,这是因为线程重入的问题,方法A调用方法B,方法A加了锁。方法B也加了锁,就会调用调用两次Lock()。就将状态进行累加。再unpark的时候,逐次递减。
在这里插入图片描述

下面这个方法就是关键点,用自旋的方式,如果自己变成了头节点,就加上锁,执行自己的逻辑。在这里插入图片描述
将线程加锁。
在这里插入图片描述

未完待续。。。。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值