AQS源码解析
*
AQS就是一个同步器,要做的事情就相当于一个锁,所以就会有两个动作:一个是获取,一个是释放。获取释放的时候该有一个东西来记住他是被用还是没被用,这个东西就是一个状态。如果锁被获取了,也就是被用了,还有很多其他的要来获取锁,总不能给全部拒绝了,这时候就需要他们排队,这里就需要一个队列。这大概就清楚了AQS的主要构成了:
自J2SE1.5开始,java中的同步类(Lock,Semphore等等)都基于AbstractQueuedSynchronizer(后文简称AQS,队列同步器)。AQS提供了一种原子式管理同步状态、阻塞和唤醒线程功能以及队列模型的简单框架。
AQS的主要使用方式是继承,子类通过继承同步器并实现它的抽象方法来管理同步状态。
AQS用32位整形来表示同步状态。
private volatile int state;
在互斥锁中表示线程是否已经获取了锁,0未获取,1已经获取,大于1表示重入数。
AQS提供了getState(),setState(),compareAndSetState()来获取和修改state的值,这些操作需要atomic包的支持,采用CAS操作,保证其原子性和可见性。
AQS使用一个int类型的成员变量state来表示同步状态,当state>0时表示已经获取了锁,当state = 0时表示释放了锁。它提供了三个方法(getState()、setState(int newState)、compareAndSetState(int expect,int update))来对同步状态state进行操作,当然AQS可以确保对state的操作是安全的。
AQS采用了java.util.concurrent.locks包下的LockSupport类。该类可以响应中断操作,可以设置超时时间等。
AQS的CLH队列锁中,每个节点代表着一个需要获取锁的线程,该node中有两个常量SHARED共享模式,EXCLUSIVE独占模式。
共享模式允许多个线程可以获取同一个锁,独占模式则一个锁只能被一个线程持有,其他线程必须要等待。
AQS框架的核心是阻塞线程的队列。也就是一个FIFO(先进先出)队列。
CLH其实就是一个FIFO的队列,只不过稍微做了点改进。AQS中内部使用内部类Node来实现,是一个链表队列,原始CLH使用自旋锁,AQS的CLH则在每个node里使用一个状态字段来控制阻塞,不是自旋。直接看代码
AQS通过内置的FIFO同步队列来完成资源获取线程的排队工作,如果当前线程获取同步状态失败(锁)时,AQS则会将当前线程以及等待状态等信息构造成一个节点(Node)并将其加入同步队列,同时会阻塞当前线程,当同步状态释放时,则会把节点中的线程唤醒,使其再次尝试获取同步状态。
AQS中的ConditionObject提供一个能让同步器使用的类。它既符合Lock接口,又能持有互斥的同步机制。
ConditionObject类提供了类似await,signal,signalAll操作的API。这些方法的作用与Object.wait方法是一样的。ConditionObject使用与同步器同样的内部队列,不过与同步器存储在分开的条件队列中。