AQS(AbstractQueuedSynchronizer)
AQS是一个同步框架,框架具有的特性
1、通用性,下层实现透明的同步机制,同时与上层业务解耦
2、利用CAS,原子的修改共享标记位
3、等待队列:两种业务场景,1、线程只想尝试获取锁,如果没有获取到就干其他的事情,2、有的业务一定要获取到共享资源才能进行下一步处理,如果当前时刻没有获取到锁它愿意等待。
第二种情况就设计一个等待队列。
线程获取锁的两种方式,独占和共享
独占模式:一旦获取锁,其他线程不能占用
共享模式:其他线程在共享的模式下可以继续占用
1、用于共享变量是否被占用的标记位
为什么state是int类型,因为在共享模式下可能有多个线程占用资源。因此state表示线程占用的数量
volatile:保证了可见性
2、等待队列FIFO双向队列
//头节点
private transient volatile Node head;
//尾节点
private transient volatile Node tail;
/**
* The synchronization state.
*/
private volatile int state;
tryacqure()方法
该方法只有一行实现,就是抛出一个异常
需要继承类重写这个方法。
给上层调用开放了空间,可以根据自己的业务编写逻辑
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
acqure()方法
final不允许修改
public final void acquire(int arg) {
/**当线程一来想要获取锁,就尝试去获取锁,如果获取锁失败就加入等待队列获取锁
在这里体现了aqs是一个非公平锁*/
//如果tryAcquire()获取锁,就跳出代码快
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
addWaiter()方法
当尝试获取锁失败后,把当前线程创建成一个node节点放在队列尾部(尝试入队)
enq并进入完整的入队方法。
private Node addWaiter(Node mode) {
//创建一个node节点
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
// 尝试快速入队,如果失败就进入完整入队方法
//获取队列尾部节点
Node pred = tail;
if (pred != null) {
node.prev = pred;
/**尝试通过cas的方式入队*/
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
enq()完整入队方法
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
acquireQueued()
队列中,
1、如果该节点处于头节点后面一个才会CAS去拿锁直到拿锁成功,
2、其他节点会被挂起,提升程序性能。
什么时候线程才会被唤醒?请看release方法
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
/***/
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
unparkSuccessor()唤醒挂起线程
当一个线程拿到锁并且执行完逻辑之后,会唤醒等待队列中的线程,去唤醒的时候倒序搜索队列,
拿锁,挂起,释放锁,唤醒形成一个良好的工作闭环。
private void unparkSuccessor(Node node) {
/**node节点就是拿到锁的线程,并且成功执行完逻辑*/
int ws = node.waitStatus;
//通过cas将状态改为默认值
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
//将node节点的下一个节点赋值给s
Node s = node.next;
/**如果s节点为空,并且等待状态为默认值*/
if (s == null || s.waitStatus > 0) {
s = null;
/**从队尾开始搜索*/
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
/**找到等待状态小于等于0的节点,赋值给s*/
s = t;
}
/**如果s节点不为空则唤醒s节点,叫他起来干活*/
if (s != null)
LockSupport.unpark(s.thread);
}