AQS(AbstractQueuedSynchronizer)框架提供了一套通用的机制来管理同步状态、阻塞/唤醒线程、管理同步队列。
一、同步机制
AQS框架描述了一个什么样的锁?
一个公司有10个人,这个公司只有一个厕所,而且是一个单人使用的厕所,厕所有门,门上有一个显示器:
当厕所无人使用时,显示器显示:
厕所状态:空闲中
使用人:无
当小明上厕所时,显示器显示:
厕所状态:使用中
使用人:小明
当小强上厕所时,显示器显示:
厕所状态:使用中
使用人:小强
...
1、锁的使用人
AbstractOwnableSynchronizer父类里定义了一个字段-拥有锁的线程,以及相应的set、get方法,相当于上面厕所使用人:
private transient Thread exclusiveOwnerThread; protected final void setExclusiveOwnerThread(Thread thread) { exclusiveOwnerThread = thread; } protected final Thread getExclusiveOwnerThread() { return exclusiveOwnerThread; }
2、锁状态
AbstractOwnableSynchronizer的子类AbstractQueuedSynchronizer里定义了锁状态,相当于上面的厕所的厕所状态:
/**同步状态 锁状态*/ private volatile int state; /**获取State*/ protected final int getState() { return state; } /**设置State*/ protected final void setState(int newState) { state = newState; } /** * CAS修改状态:当前值和期望值相同时 原子性的修改更新值 * 返回true说明修改成功 返回false说明当前值和期望值不相同 */ protected final boolean compareAndSetState(int expect, int update) { return unsafe.compareAndSwapInt(this, stateOffset, expect, update); }
锁就是线程竞争的资源,AQS使用int变量state来表示这个资源状态(同步状态),这与synchronize内部锁(底层锁mutex)是不一样的,这里叫同步状态比锁更合适一些。
独占模式:比如ReentrantLock,初始state=0,某一个线程获取到同步状态state+1,释放同步状态state-1;
共享模式:比如Semaphore/CountDownLatch,多个线程都会获取到同步状态;
二、内部类Node
Node类被设计作为同步队列(CLH)和条件队列(Condition)的节点,条件队列只限独占模式下使用。
源码
static final class Node { /** 静态属性:标记节点为共享模式*/ static final Node SHARED = new Node(); /** 静态属性:标记节点为独占模式*/ static final Node EXCLUSIVE = null; /** 静态属性:waitStatus的值 表明线程已取消 当前线程因为超时或者中断被取消。这是一个终结态,也就是状态到此为止。*/ static final int CANCELLED = 1; /** 静态属性:waitStatus的值 表明后继节点中的线程需要唤醒 用于独占锁 * 当前线程的后继线程被阻塞或者即将被阻塞,当前线程释放锁或者取消后需要唤醒后继线程。这个状态一般都是后继线程来设置前驱节点的。 */ static final int SIGNAL = -1; /** 静态属性:waitStatus的值 当前节点需要进入等待队列 用于独占锁*/ static final int CONDITION = -2; /**静态属性:waitStatus的值 下一个acquireShared方法被允许 用于共享锁 * 用于将唤醒后继线程传递下去,这个状态的引入是为了完善和增强共享锁的唤醒机制。在一个节点成为头节点之前,是不会跃迁为此状态的 */ static final int PROPAGATE = -3; /** * Status field, taking on only the values: * SIGNAL: 发信号。表示后继结点被阻塞了(当前结点在入队后、阻塞前,应确保将其prev结点类型改为SIGNAL,以便prev结点取消或释放时将当前结点唤醒。) * CANCELLED: 取消。表示后继结点被中断或超时,需要移出队列 * CONDITION: 节点处于等待队列 当转向同步队列时 将改为0 * PROPAGATE: 传播 * 0: 初始状态 * 初始化状态0用于同步队列;初始化-2用于等待队列,并通过CAS方法来修改 */ volatile int waitStatus; /**当前节点的前一个节点,用于维持同步队列(CLH)*/ volatile Node prev; /**当前节点的下一个节点,用于维持同步队列(CLH)*/ volatile Node next; /**当前节点包裹的线程*/ volatile Thread thread; /** * 如果是共享模式:nextWaiter=SHARED * 如果是独占模式:nextWaiter为等待队列的下一个节点 */ Node nextWaiter; /** 根据nextWaiter判断当前节点是否是共享模式*/ final boolean isShared() { return nextWaiter == SHARED; } /** 返回当前节点的前一个节点*/ final Node predecessor() throws NullPointerException { Node p = prev; if (p == null) throw new NullPointerException(); else return p; } /**无参构造器 用来确立初始头节点或共享模式 */ Node() { // Used to establish initial head or SHARED marker } /**用在addWaiter方法里 向CLH队列添加新节点的构造器*/ Node(Thread thread, Node mode) { // Used by addWaiter this.nextWaiter = mode; this.thread = thread; } /**用在Condition里向等待队列添加新节点的构造器*/ Node(Thread thread, int waitStatus) { // Used by Condition this.waitStatus = waitStatus; this.thread = thread; } }
1、模式
2、状态
3、4个链接引用