什么是AQS?
即AbstractQueuedSynchronizer,队列同步器,是用来构建锁或者其他同步器组件的重量级基础框架及整个JUC体系的基石,通过内置的FIFO队列来完成资源获取线程的排队工作,并通过一个int类型的变量表示持有锁的状态;当有线程获取不到锁时,就将线程加入该队列中,通过CAS自旋和LockSupport.part()的方式,维护state变量,达到并发同步的效果。
其中,
ReentranLock
,CountDownLatch
,ReentrantReadWriteLock``,Semaphore
底层都是AQS
原理:AQS使用一个violatile的int类型的成员变量来表示同步状态,通过内置的FIFO队列来完成资源获取的排队工作,将每条要抢占资源的线程封装成一个Node节点来完成锁的分配,通过CAS完成对state值的修改;
AQS的核心思想是什么?
AQS核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中
CLH
是Craig
,Landin
andHagersten
队列,是一个单向链表,AQS中的队列是CLH扩展的虚拟双向FIFO队列;
公平锁与非公平锁的区别
公平锁:多个线程按照线程调用lock()的顺序去获得锁,线程会直接进入队列去排队,永远都是队列的第一位才能得到锁。
优点:所有的线程都能得到资源,不会饿死在队列中。
缺点:吞吐量会下降很多,队列里面除了第一个线程,其他的线程都会阻塞,cpu唤醒阻塞线程的开销会很大。
非公平锁:多个线程去获取锁的时候,会直接去尝试获取,获取不到,再去进入等待队列,如果能获取到,就直接获取到锁。优点:可以减少CPU唤醒线程的开销,整体的吞吐效率会高点,CPU也不必取唤醒所有线程,会减少唤起线程的数量。
缺点:这样可能导致队列中间的线程一直获取不到锁或者长时间获取不到锁,导致饿死。
AQS实现方式:
独占式和共享式
先从ReentrantLock#lock
方法开始。
该方法定义如下
/**
* Acquires the lock.
*
* <p>Acquires the lock if it is not held by another thread and returns
* immediately, setting the lock hold count to one.
*
* <p>If the current thread already holds the lock then the hold
* count is incremented by one and the method returns immediately.
*
* <p>If the lock is held by another thread then the
* current thread becomes disabled for thread scheduling
* purposes and lies dormant until the lock has been acquired,
* at which time the lock hold count is set to one.
*/
public void lock() {
sync.lock();
}
其中sync是AbstractQueuedSynchronizer的实现,我们知道,ReentrantLock支持公平锁和非公平锁,其实现类分别是FairSync和NonfairSync
// FairSync 公平锁的实现
final void lock() {
acquire(1);
}
// NonfairSync 非公平锁的实现
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
可以看到,非公平锁的实现仅仅是多了一个步骤:通过CAS的方式(compareAndSetState)尝试改变state的状态,修改成功后设置当前线程以独占的方式获取了锁,修改失败执行的逻辑和公平锁一样。