AQS
AQS的核心也包括了这些方面:同步队列,独占式锁的获取和释放,共享锁的获取和释放以及可中断锁,超时等待锁获取这些特性的实现
1.同步队列
AQS中的同步队列则是通过链式方式进行实现(双向链表没一个节点都拥有前驱和后继节点),使用头尾指针的方式管理同步队列。
获取锁失败进行入队操作,获取锁成功进行出队操作。
2.独占锁
(1)获取独占锁【acquire方法】
调用lock()方法获取独占锁,获取失败就将当前线程加入同步队列,成功则线程执行,实际上Lock()方法会调用AQS的acquire()方法
A.成功则方法结束返回
B. 失败则先调用addWaiter() 然后调用acquireQueued()方法进入同步队列等待
(2)获取失败进入等同步队列
private Node addWaiter(Node mode) {
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;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
当前线程的信息存放到一个新的Node节点,当前链表尾节点不为空直接进行尾插入,compareAndSetTail是一个CAS操作操作失败会进入自旋进行重试,
如果当前队列为null执行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;
}
}
}
}
enq操作视为当前没有队列
1.重新穿件一个头结点 t
2.当t==null执行插入
(3)acquireQueued()方法 排队获取锁
1.获取当前及诶单的先驱节点
2.判断当前节点的先驱节点势头及诶单并且成功获取同步状态,即可以获取独占锁
3.将队列的头指针指向当前节点
4.释放前驱节点 (前驱节点已经获取独占锁)
5.获取失败 进入等待状态继续获取独占锁
总结
图片来源:https://juejin.im/post/5aeb07ab6fb9a07ac36350c8
1.线程获取锁失败,线程被封装成Node进行入队操作,核心方法在于addWaiter()和enq(),同时enq()完成对同步队列的头结点初始化工作以及CAS操作失败的重试;
2.线程获取锁是一个自旋的过程,当且仅当 当前节点的前驱节点是头结点并且成功获得同步状态时,节点出队即该节点引用的线程获得锁,否则,当不满足条件时就会调用LookSupport.park()方法使得线程阻塞;
3.释放锁的时候会唤醒后继节点;
3.可中断获取锁(acquireInterrutibly)
public final void acquireInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (!tryAcquire(arg))
//线程获取锁失败
doAcquireInterruptibly(arg);
}
判断获取所失败则会调用doAcquireInterruptibly
将当前节点同步到队列中
循环获取队列的头结点出队,获取成功将当前头结点移动下节点
4.超时等待获取锁(tryAcquireNanos)
1.在超时时间内,当前线程成功获取了锁;
2.当前线程在超时时间内被中断;
3.超时时间结束,仍未获得锁返回false。
public final boolean tryAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
return tryAcquire(arg) ||
//实现超时等待的效果
doAcquireNanos(arg, nanosTimeout);
}
doAcquireNanos
1.根据超时时间和当前时间计算出截止时间
2.当前线程获得锁出队列
3.重新计算超时时间
4.线程阻塞等待
5.线程中断抛出异常
5.共享锁(acquireShared)
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
tryAcquireShared返回一个int类型大于等于0成功获取锁 小于零获取失败执行doAcquireShared() 与独占锁类似获取当前队列判断是否为null将获取锁对象的线程变为Node,尾插入队列成功进入同步队列失败则CAS自旋
进入同步队列之后若为投机诶单则获取同步状态,不为头则插入等待获取