AQS 的全称为(AbstractQueuedSynchronizer),是一个用来构建锁和同步器的框架,使用 AQS 能简单且高效地构造出同步器, 如ReentrantLock,Semaphore,ReentrantReadWriteLock ,SynchronousQueue等。当然,我们自己也能利用 AQS 非常轻松容易地构造出符合我们自己需求的同步器。
AQS 核心思想通过共享资源状态(State)与阻塞对队列(Queue)实现互斥与共享,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就将该请求线程加入阻塞队列,等待唤醒。AQS 是用 CLH 队列*锁实现的,即将暂时获取不到锁的线程加入到队列中。
*CLH是一个FIFO得同步双端队列
使用volatile修饰一个 int 成员变量来表示同步状态,并保证以原子操作修改该值,通过内置的 FIFO 队列来完成获取资源线程的排队工作。
state通过三个protected修饰得方法进行操作getState(),setState(),compareAndSetState()
AQS包含独占(Exclusive)与共享(Share)两种资源共享方式,即ReentrantLock与Smaphore实现基础,接下来分别以ReentrantLock与Smaphore讲解AQS两种资源共享方式。
独占模式即同一时刻只有一个线程能执行,如 ReentrantLock。而独占锁内部根据阻塞进程唤醒方式不同,又可分为公平锁和非公平锁。
RenntrantLock考虑获得更好得性能,默认采用非公平锁,其采用模式可通过boolean来决定。
其获取锁得具体流程为:
其中,CLH队列节点Node,通过waitStatus变量记录,该任务线程状态,其状态为:
公平获取锁:
非公平获取锁
注:
1、非公平锁在调用lock后,首先会调用CAS进行一次抢锁,如果这时恰巧没被占用,则直接获取并返回;
2、非公平锁在CAS失败后,和公平锁一样进入到tryAcquire方法中,在tryAcquire中,若此时锁释放,非公平锁直接CAS抢锁,而公平锁会判断等待队列是否有线程处于等待状态,有则会排到队尾。
其释放锁得具体流程为:
共享模式即可以指定多个线程同时访问某个资源,如Semaphore。与独占方式一样,内部根据阻塞进程唤醒方式不同,又可分为公平锁和非公平锁,分别对应两种构造函数,相较于独占方式,两个构造方法,都必须额外提供一个int变量permits,表示可共享得资源个数。
当执行任务的线程数量超出permits,则多余的线程将会被放入阻塞队列Park,并自旋判断state是否大于0。只有当state大于0的时候,阻塞的线程才能继续执行,此时先前执行任务的线程继续执行release方法,release方法使得state的变量会加1,那么自旋的线程便会判断成功。如此,每次只有最多不超过permits数量的线程能自旋成功,便限制了执行任务线程的数量。
其获取锁得具体流程为:
其释放锁得具体流程为:
AQS 使用了模板方法模式,自定义同步器时需要实现如下几个 AQS 提供的模板方法:
(1)isHeldExclusively //该线程是否独占资源
(2)tryAcquire(int) //独占方法,获取锁
(3)tryRelease(int) //独占方式,释放锁
(4)tryAcquireShared(int) //共享方法,获取锁
(5)tryReleaseShared(int) //共享方式,释放锁