1.AQS简介
Java并发编程核心在于java.concurrent.util包而juc当中的大多数同步器实现都是围绕着共同的基础行为,比如等待队列、条件队列、独占获取、共享获取等,而这个行为的抽象就是基于AbstractQueuedSynchronizer简称AQS,AQS定义了一套多线程访问共享资源的同步器框架,是一个依赖状态(state)的同步器。
2.AQS特性
AQS具备特性:阻塞等待队列,共享/独占,公平/非公平,可重入,允许中断
3.AQS的组成
四个个成员变量
private transient Thread exclusiveOwnerThread; 独占模式下的当前线程
private transient volatile Node head; 队列的头部指针
private transient volatile Node tail; 队列的尾部指针
private volatile int state;
一个内部类
static final class Node {
模式
static final Node SHARED = new Node(); 共享模式
static final Node EXCLUSIVE = null; 独占模式
信号量-线程的生命状态
static final int CANCELLED = 1; 出现异常
static final int SIGNAL = -1; 可被唤醒,同步队列中通知后继节点
static final int CONDITION = -2; 条件等待
static final int PROPAGATE = -3; 传播 Semaphore 共享模式下
volatile int waitStatus;
链表指针
volatile Node prev;
volatile Node next;
Node nextWaiter;
节点数据(线程)
volatile Thread thread;
以上属性在ReentrantLock,Semaphore等并发工具下都有不同使用方式。
4.如何加锁
AQS里面通过一个状态同步器来加锁
/**
* PS : 加锁的次数
* 同步资源状态
*/
private volatile int state;
同一个线程每次加锁都是对state+1,如果要保证并发安全就需要使用CAS+自旋+volatile修饰
5.没有抢到锁的线程到哪里去了
//双向链表构建的队列
static final class Node {
volatile Node prev;
volatile Node next;
volatile Thread thread;
}
上面提到的node节点构建的FIFO队列,就是在lock方法加锁时候,用来保存没有抢到锁的阻塞线程,以及unlock方法从头开始根据信号量去唤醒阻塞的线程。
6.总结
通过对AbstractQueuedSynchronizer类的了解,以及如何加锁解锁,线程的去向问题,对AQS有一个基本了解,保证并发主要通过state,node,CAS,volatile搭配。下面画一张图更形象的了解。后面再写reentrantlock