最好懂的AQS核心源码
相必大家应该都在各个八股文里听过AQS吧。只要你简历里写上你会多线程,这个知识点几乎是必问的。
在阅读之前,读者希望你们对ReentrantLock和CountDownLounch()两个类有过使用经历。
什么是AQS?
AQS的全称就是 AbstractQueuedSynchronizer,翻译过来就是抽象队列同步器,这是JUC包中的一个类。
这个类很关键,很多并发工具类都是基于这个类实现的,他也是JUC同步框架的基石。
实现类需要去继承该类,并重写指定方法就可以实现一套线程同步机制。
我们经常用到的ReentrantLock和CountDownLatch等也都是基于AQS实现的。
从生活角度理解AQS
AQS的设计就和取钱的场景很类似,1号,2号,3号选手都在银行取钱,柜台只有一个,因此同一时间只有一个人可以取钱。1号在取钱的时候,2号,3号都会在等待区等待。1号取完后,会通知2号,2号再去取。
AQS框架
AQS核心
AQS的核心只有两个东西:
- 一个用volatile修饰的state变量(最后有讲解)
- 一个CLH(三个人名缩写)的双向队列。
每一个线程获取锁的时候,就会试图去修改state变量,如果修改成功就能获取锁,如果失败,就自动加入双向队列的末尾,等待持有锁的线程对其进行释放。
这里就可以理解成银行取钱,只有一张票,获得票的人可以取钱,取完后,把这个票给另一个在等待的人,另一个人就能取钱了
其中,AQS对state的访问设置了三种方法:
- getState()
- setState()
- compareAndSetState()
其中compareAndSetState 就是 CAS 法的思想体现。它结合了CAS自旋 volatile 变量(最后有讲解)。
两种资源共享方式
AQS定义了两种资源共享方式:Exclusive(独占的,仅一个线程可以执行,如ReentrantLock)和Share(共享的,如CountDownLatch)
不同自定义同步器争用共享资源的方式不同。在实现自定义同步器的时候,只需要实现state的释放和获取的方式即可。
像ReentrantLock,CountDownLatch都是jdk里实现了以下方法的自定义同步器
自定义同步器需要实现以下几种方法:
独占式(比如ReentrantLock)
- tryAcquire(int) : 独占的资源共享方式。尝试获取资源,成功返回true,失败返回false
- tryRelease(int) : 独占的资源共享方式。尝试释放资源,成功返回true,失败返回false
共享式(比如CountDownLatch)
- tryAcquireShared(int):共享的资源共享方式。尝试获取资源,负数表示失败,0表示成功,但没有剩余资源,正数表示成功,有剩余资源。
- tryReleaseShared(int):共享的资源共享方式。尝试释放资源。
ReentrantLock为例(独占)
以ReentrantLock为例,state初始化是0,表示未锁定状态。A线程调用ReentrantLock的lock()方法时,同时会调用tryAcquire()方法,抢占锁,并且将state+1。此后所有的线程去执行tryRelease方法都会失败,直到A线程调用ReentrantLock的unLock()方法,也就是调用ryRelease(int)方法,state会变成0,之后其他线程才能获得到锁。当然A线程在锁的时候,也是可以重复获取锁的,每重复获取一次,state+1,在释放的时候,获得多少次就要减多少次。
CountDownLatch为例(共享)
以CountDownLatch为例,当你在构造器里面传入一个参数n的时候,state也会被初始化成n。每个